SlideShare une entreprise Scribd logo
1  sur  56
10월 24일
제 생일 -_-;;
자전거 타기
균형을 잡아야지!
자빠지지 말아야지! 라고 외우면…
학습
훈련암기
테스트, 암기하면 되는가?
학습
욕망은 여기서 꿈틀 테스트
훈련암기
테스트는 암기가 아니라 훈련이다.
테스트에는 많은 장애물을 있을텐데...
장애물 없는 길로 다닐 수 없을까요?
테스트하기 쉬운 코드로
개발하기
• 정진욱
• 백앤드
• 언어: F# / C#
• 운영환경: Azure
• 아키텍처: CQRS
(with Event Sourcing)
• 정진욱
• 최근 관심 분야
• Domain Modeling Made Functional
• Property-based testing
• 페이스북
• jinwook.chung.167
• 이메일
• jwchung@hotmail.com
• 블로그
• https://jwchung.github.io
예제 코드를 C#으로 작성하였습니다.
C#에 익숙하지 않으시다면…
다음과 같은 용어가 사용됩니다.
- CQS
- 비동기: Task, async await
- Test Double
- Dependency Injection
혹시 이해가 안되는 용어가 나오면, 해당 용어에 얽매이기 보다
전체를 맥락을 이해하는 것에 중점을 두세요.
테스트하기 쉬운 코드란?
테스트 장애물은?
불확실성(non-deterministic)
• 외부세상에서 값 읽어오기
• 랜덤수 / 임의시각
• 전역변수
• 로컬머신에 존재하는 파일 내용
• 데이터베이스의 특정 레코드
• HTTP - GET
장애물1
public string GetAMOrPM()
{
var now = DateTime.Now;
if (now.Hour < 12)
{
return "AM";
}
else
{
return "PM";
}
}
부수효과(side-effect)
• 외부세상의 값을 변경
• 전역변수
• 로컬머신에 존재하는 파일 내용
• 데이터베이스의 특정 레코드
• HTTP - POST
장애물2
Arrange
Act
Assert
SUT
(테스트 대상)
Database
순수함수(pure function)
• 불확실성: 외부세상에서 값을 읽어오는 것과 관련
• 부수효과: 외부세상에 값을 기록하는 것과 관련
• 불확실성과 부수효과가 없는 것을 순수함수라 함
테스트하기 쉬운 코드외부 세상과 단절된 상태
리턴 타입 별 Testability
• 리턴 타입이 없는 경우
public void Add(int x, int y)
{
... // 외부세상을 변경하는 코드
}
리턴 타입 별 Testability
• 리턴 타입이 있는 경우
외부세상을 변경하는 코드 존재
public int Add(int x, int y)
{
var result = x + y;
Console.WriteLine(result);
return result;
}
Non-testable
리턴 타입 별 Testability
• 리턴 타입이 있는 경우
public int Add(int x, int y)
{
var result = x + y;
return result;
}
리턴 타입 별 Testability
• 리턴 타입이 있는 경우
public int Add(int x, int y)
{
int result = MathApiClient.GetAdd(x, y);
return result;
}
하지만 외부세상에 의존하면?
외부세상에 의존하지 않고
값을 리턴하는 경우
테스트하기 쉬운 코드란?
하스켈: IO<T>
C#: Task<T>
자바: Future<T>
IO<T>를 리턴하지 않으면서 Non-testable한 경우도 있지만(eg. 랜덤수),
큰 틀에서 IO<T>를 리턴하는 경우를 Non-testable 이다고 할 수 있음.
예제 시나리오(회원가입)
1. 입력된 이메일 형식을 검사한다.
2. 입력된 비밀번호 형식을 검사한다.
3. 정보를 DB에 저장하고 회원가입을 완료한다.
public async Task SignUp(string email, string password)
{
// 이메일이 유효한지 검사합니다.
if (!email.Contains("@"))
throw new ArgumentException("유효한 이메일 형식이 아닙니다.");
...
// 비밀번호가 유효한지 검사합니다.
if (password.Length < 8)
throw new ArgumentException("비밀번호는 최소 8자리 이상입니다.");
...
await UserStore.AddAsync(email, passwod);
}
테스트하기 쉬운 코드입니까?
Non-testable 무엇이 문제입니까?
public async Task SignUp(string email, string password)
{
// 이메일이 유효한지 검사합니다.
if (!email.Contains("@"))
throw new ArgumentException("유효한 이메일 형식이 아닙니다.");
...
// 비밀번호가 유효한지 검사합니다.
if (password.Length < 8)
throw new ArgumentException("비밀번호는 최소 8자리 이상입니다.");
...
await UserStore.AddAsync(email, passwod);
}
어떻게 비용을 낮출 수 있을까요?
테스트하기 쉬운 코드로 개발하기
1. Testable과 Non-testable 코드를 최대한 분리한다.
Testable
Non-testable
SignUp
Email/Password Class
UserStore.AddSync
public class Email
{
public Email(string value)
{
// 이메일이 유효한지 검사합니다.
if (!value.Contains("@"))
throw new ArgumentException("유효한 이메일 형식이 아닙니다.");
...
this.Value = value;
}
public string Value { get; }
public static bool TryParse(string value, out Email email)
{
try
{
var email = new Email(value);
return true;
}
catch (ArgumentException)
{
email = null;
return false;
}
}
}
Email/Password Class
UserStore.AddSync
Testable Non-Testable
요구사항을 구현하려면 이 둘은 어디선가 만나야 합니다.
Testable
Testable
Testable
Method Call Tree
Non-Testable
Testable
Testable
Non-Testable
Method Call Tree
Non-Testable
Testable
Non-Testable
Non-Testable
Method Call Tree
Non-Testable
Non-Testable
Non-Testable
Non-Testable
Method Call Tree
Non-Testable
어디서 만나야 Testable 코드를
작성할 수 있을까요?
최대한 많이
Testable
Testable
Testable
Method Call Tree
Non-Testable
Boundary Layer
테스트하기 쉬운 코드로 개발하기
1. Testable과 Non-testable 코드를 최대한 분리한다.
2. Testable과 Non-testable 코드는 Boundary Layer에서
만나게 한다.
Boundary Layer
• UI 프로그램의 이벤트 핸들러
• Web API의 액션메소드
• 콘솔 프로그램 메인메소드
• Etc
테스트하기 쉬운 코드로 개발하기
1. Testable과 Non-testable 코드를 최대한 분리한다.
2. Testable과 Non-testable 코드는 Boundary Layer에서
만나게 한다.
3. Boundary Layer 테스트 방법을 익힌다.
Boundary Layer 테스트란?
public class AccountController : ApiController
{
[HttpPost]
public async Task<IHttpActionResult> SignUpAsync(
string email, string password)
{
try
{
var emailObj = new Email(email);
var passwordObj = new Password(password);
await new UserStore().AddAsync(emailObj, passwordObj);
await new EmailConfirmation().SendAsync(emailObj);
return this.Ok();
}
catch (ArgumentException exception)
{
return this.BadRequest(exception.Message);
}
}
}
단위테스트 완료!
수동/통합테스트 완료!
Boundary Layer를 단위테스트하는
방법은?
public class AccountController : ApiController
{
public AccountController(
IUserStore userStore, IEmailConfirmation emailConfirmation)
{
this.UserStore = userStore
?? throw new ArgumentNullException(nameof(userStore));
this.EmailConfirmation = emailConfirmation
?? throw new ArgumentNullException(nameof(emailConfirmation));
}
public IUserStore UserStore { get; }
public IEmailConfirmation EmailConfirmation { get; }
[HttpPost]
public async Task<IHttpActionResult> SignUpAsync(
string email, string password)
{
try
{
var emailObj = new Email(email);
var passwordObj = new Password(password);
await this.UserStore.AddAsync(emailObj, passwordObj);
await this.EmailConfirmation.SendAsync(emailObj);
return this.Ok();
}
catch (ArgumentException exception)
{
return this.BadRequest(exception.Message);
}
}
}
public class UserStoreSpy : IUserStore
{
public Email Email { get; set; }
public Password Password { get; set; }
public Task AddAsync(Email email, Password password)
{
this.Email = email;
this.Password = password;
return Task.FromResult<object>(null); // 빈 Task 반환
}
}
public class EmailConfirmationSpy : IEmailConfirmation
{
public Email Email { get; set; }
public Task SendAsync(Email email)
{
this.Email = email;
return Task.FromResult<object>(null); // 빈 Task 반환
}
}
[Fact]
public async Task SignUpAsyncWithValidEmailAndPassowordReturnsOkResult()
{
// Arrange
var userStoreSpy = new UserStoreSpy();
var emailConfirmationSpy = new EmailConfirmationSpy();
var sut = new AccountController(userStoreSpy, emailConfirmationSpy);
string email = "jwchung@hotmail.com";
string password = "P@assW0rd";
// Act
await sut.SignUpAsync(email, password);
// Assert
Assert.Equal(new Email(email), userStoreSpy.Email);
Assert.Equals(new Password(password), userStoreSpy.Password);
Assert.Equal(new Email(email), emailConfirmationSpy.Email);
}
Boundary Layer 테스트 방법
• 수동테스트
• 인수테스트
• 단위테스트
정리
테스트하기 쉬운 코드로 개발하는 방법?
정리
1. Testable과 Non-testable 코드를 최대한 분리한다.
Domain Models Services
Email Class
Password Class
UserStore
EmailConformation
단위테스트 수동테스트 / 통합테스트
정리
2. Testable과 Non-testable 코드는
Boundary Layer에서 만나게 한다.
Domain Models Services
Boundary Layer
Domain Models
내가 작성한 코드가
처음 실행되는 곳
public class UserStoreSpy : IUserStore
{
public Email Email { get; set; }
public Password Password { get; set; }
public Task AddAsync(Email email, Password password)
{
this.Email = email;
this.Password = password;
return Task.FromResult<object>(null); // 빈 Task 반환
}
}
public class EmailConfirmationSpy : IEmailConfirmation
{
public Email Email { get; set; }
public Task SendAsync(Email email)
{
this.Email = email;
return Task.FromResult<object>(null); // 빈 Task 반환
}
}
정리
3. Boundary Layer 테스트 방법을 익힌다.
Boundary Layer
수동테스트
인수테스트
단위테스트
감사합니다.

Contenu connexe

Tendances

日本語テストメソッドについて
日本語テストメソッドについて日本語テストメソッドについて
日本語テストメソッドについて
kumake
 
pytest로 파이썬 코드 테스트하기
pytest로 파이썬 코드 테스트하기pytest로 파이썬 코드 테스트하기
pytest로 파이썬 코드 테스트하기
Yeongseon Choe
 
社内Java8勉強会 ラムダ式とストリームAPI
社内Java8勉強会 ラムダ式とストリームAPI社内Java8勉強会 ラムダ式とストリームAPI
社内Java8勉強会 ラムダ式とストリームAPI
Akihiro Ikezoe
 

Tendances (20)

SQL大量発行処理をいかにして高速化するか
SQL大量発行処理をいかにして高速化するかSQL大量発行処理をいかにして高速化するか
SQL大量発行処理をいかにして高速化するか
 
The Java memory model made easy
The Java memory model made easyThe Java memory model made easy
The Java memory model made easy
 
20160526 依存関係逆転の原則
20160526 依存関係逆転の原則20160526 依存関係逆転の原則
20160526 依存関係逆転の原則
 
初心者向けMongoDBのキホン!
初心者向けMongoDBのキホン!初心者向けMongoDBのキホン!
初心者向けMongoDBのキホン!
 
AWSのログ管理ベストプラクティス
AWSのログ管理ベストプラクティスAWSのログ管理ベストプラクティス
AWSのログ管理ベストプラクティス
 
Java ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugJava ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsug
 
A5 SQL Mk-2の便利な機能をお教えします
A5 SQL Mk-2の便利な機能をお教えしますA5 SQL Mk-2の便利な機能をお教えします
A5 SQL Mk-2の便利な機能をお教えします
 
Where狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキーWhere狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキー
 
MongoDBの監視
MongoDBの監視MongoDBの監視
MongoDBの監視
 
イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)
 
日本語テストメソッドについて
日本語テストメソッドについて日本語テストメソッドについて
日本語テストメソッドについて
 
Node.js API 서버 성능 개선기
Node.js API 서버 성능 개선기Node.js API 서버 성능 개선기
Node.js API 서버 성능 개선기
 
基礎*Force(セキュリティに気をつけてforce.comで開発しよう!)
基礎*Force(セキュリティに気をつけてforce.comで開発しよう!)基礎*Force(セキュリティに気をつけてforce.comで開発しよう!)
基礎*Force(セキュリティに気をつけてforce.comで開発しよう!)
 
elasticsearch_적용 및 활용_정리
elasticsearch_적용 및 활용_정리elasticsearch_적용 및 활용_정리
elasticsearch_적용 및 활용_정리
 
さわってみようTOPPERS/SSP
さわってみようTOPPERS/SSPさわってみようTOPPERS/SSP
さわってみようTOPPERS/SSP
 
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
 
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
 
pytest로 파이썬 코드 테스트하기
pytest로 파이썬 코드 테스트하기pytest로 파이썬 코드 테스트하기
pytest로 파이썬 코드 테스트하기
 
#살아있다 #자프링외길12년차 #코프링2개월생존기
#살아있다 #자프링외길12년차 #코프링2개월생존기#살아있다 #자프링외길12년차 #코프링2개월생존기
#살아있다 #자프링외길12년차 #코프링2개월생존기
 
社内Java8勉強会 ラムダ式とストリームAPI
社内Java8勉強会 ラムダ式とストリームAPI社内Java8勉強会 ラムダ式とストリームAPI
社内Java8勉強会 ラムダ式とストリームAPI
 

Similaire à [OKKY 세미나] 정진욱 - 테스트하기 쉬운 코드로 개발하기

Effective unit testing ch3. 테스트더블
Effective unit testing   ch3. 테스트더블Effective unit testing   ch3. 테스트더블
Effective unit testing ch3. 테스트더블
YongEun Choi
 
테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)
테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)
테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)
Suwon Chae
 
C++ 프로젝트에 단위 테스트 도입하기
C++ 프로젝트에 단위 테스트 도입하기C++ 프로젝트에 단위 테스트 도입하기
C++ 프로젝트에 단위 테스트 도입하기
Heo Seungwook
 
카사 공개세미나1회 W.E.L.C.
카사 공개세미나1회  W.E.L.C.카사 공개세미나1회  W.E.L.C.
카사 공개세미나1회 W.E.L.C.
Ryan Park
 
TDD&Refactoring Day 03: TDD
TDD&Refactoring Day 03: TDDTDD&Refactoring Day 03: TDD
TDD&Refactoring Day 03: TDD
Suwon Chae
 
레일스를 이용한 애자일 웹 개발 가이드
레일스를 이용한 애자일 웹 개발 가이드레일스를 이용한 애자일 웹 개발 가이드
레일스를 이용한 애자일 웹 개발 가이드
Sukjoon Kim
 

Similaire à [OKKY 세미나] 정진욱 - 테스트하기 쉬운 코드로 개발하기 (20)

Spring Boot 2
Spring Boot 2Spring Boot 2
Spring Boot 2
 
TDD.JUnit.조금더.알기
TDD.JUnit.조금더.알기TDD.JUnit.조금더.알기
TDD.JUnit.조금더.알기
 
Okjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 TestOkjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 Test
 
10장 결과 검증
10장 결과 검증10장 결과 검증
10장 결과 검증
 
katalon studio 툴을 이용한 GUI 테스트 자동화 가이드
katalon studio 툴을 이용한 GUI 테스트 자동화 가이드katalon studio 툴을 이용한 GUI 테스트 자동화 가이드
katalon studio 툴을 이용한 GUI 테스트 자동화 가이드
 
TDD - Test Driven Development
TDD - Test Driven DevelopmentTDD - Test Driven Development
TDD - Test Driven Development
 
Working Effectively With Legacy Code - xp2005
Working Effectively With Legacy Code - xp2005Working Effectively With Legacy Code - xp2005
Working Effectively With Legacy Code - xp2005
 
Effective unit testing ch3. 테스트더블
Effective unit testing   ch3. 테스트더블Effective unit testing   ch3. 테스트더블
Effective unit testing ch3. 테스트더블
 
테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)
테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)
테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)
 
C++ 프로젝트에 단위 테스트 도입하기
C++ 프로젝트에 단위 테스트 도입하기C++ 프로젝트에 단위 테스트 도입하기
C++ 프로젝트에 단위 테스트 도입하기
 
자바 테스트 자동화
자바 테스트 자동화자바 테스트 자동화
자바 테스트 자동화
 
카사 공개세미나1회 W.E.L.C.
카사 공개세미나1회  W.E.L.C.카사 공개세미나1회  W.E.L.C.
카사 공개세미나1회 W.E.L.C.
 
TDD&Refactoring Day 03: TDD
TDD&Refactoring Day 03: TDDTDD&Refactoring Day 03: TDD
TDD&Refactoring Day 03: TDD
 
테스터가 말하는 테스트코드 작성 팁과 사례
테스터가 말하는 테스트코드 작성 팁과 사례테스터가 말하는 테스트코드 작성 팁과 사례
테스터가 말하는 테스트코드 작성 팁과 사례
 
레일스를 이용한 애자일 웹 개발 가이드
레일스를 이용한 애자일 웹 개발 가이드레일스를 이용한 애자일 웹 개발 가이드
레일스를 이용한 애자일 웹 개발 가이드
 
Cygnus unit test
Cygnus unit testCygnus unit test
Cygnus unit test
 
[D2 COMMUNITY] ECMAScript 2015 S67 seminar - 1. primitive
[D2 COMMUNITY] ECMAScript 2015 S67 seminar - 1. primitive[D2 COMMUNITY] ECMAScript 2015 S67 seminar - 1. primitive
[D2 COMMUNITY] ECMAScript 2015 S67 seminar - 1. primitive
 
Sonarqube 20160509
Sonarqube 20160509Sonarqube 20160509
Sonarqube 20160509
 
Droid knights android test @Droid Knights 2018
Droid knights android test @Droid Knights 2018Droid knights android test @Droid Knights 2018
Droid knights android test @Droid Knights 2018
 
08장 객체와 클래스 (기본)
08장 객체와 클래스 (기본)08장 객체와 클래스 (기본)
08장 객체와 클래스 (기본)
 

[OKKY 세미나] 정진욱 - 테스트하기 쉬운 코드로 개발하기