반응형

가독성을 높이는 습관 - 테스트 코드 (2)

 

 

테스트 코드의 딜레마

  • 처음 테스트 코드를 작성하려고 할 때 겪게 되는 일
  • 테스트 코드를 작성하려고하니, 원하는 테스트를 작성하기가 어려움
  • 쉽게 테스트 코드를 만들려면? -> 테스트 가능한 구조로 작성되어 있어야함
  • 테스트 가능한 구조를 만들려면? -> 좋은 구조 / 코드가 무엇인지 알아야함
  • 좋은 구조 / 코드로 작성되었다면? -> 어떤 테스트를 작성해야 할지 알아야함

 

 

무엇을 테스트 해야 할까?

  • 성송하는 테스트?
  • 파라미터?
  • 리턴값?
  • 호출되는 함수들?
  • private / public 메서드?
  • 구체적인 것이 아니라 추상적인 것을 테스트 해야함

 

 

데이터(상태) 테스트

//상태 검증
@Test
void test1() {
    Something something = new Something();
    something.foo();
    
    assertThat(something.getStatus()).isTrue();
}
//파라미터 검증
@Test
void test3() {
    Something something = mock(Something.class);
    ArgumentCaptor<String> stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
    
    something.foo("hello");
    
    verify(something).foo(stringArgumentCaptor.capture());
    String value = stringArgumentCaptor.getValue();
    assertThat(value).isEqualTo("hello");
}

 

 

행위 테스트

//행위 검증
@Test
void test2() {
    Something something = mockito.mock(Something.class);
    
    something.foo();
    
    verify(something).foo();
    verity(something, never()).bar();
}

 

 

성공하는 테스트

@Test
void test2() {
    ProductStock productStock = new ProductStock();
    productStock.setStock(0);
    
    assertThat(productStock.isSale()).isFalse();
}

 

 

실패하는 테스트

@Test
void test2() {
    ProductStock productStock = new ProductStock();
    productStock.setStock(0);
    
    assertThatThroenBy(() -> productStock.decreaseStock(10))
            .isInstanceOf(RuntimeException.class);
}

 

 

테스트하기 어려운 것 - 시간

public PaymentResult payment(Order order) {
    return isPeperoDay(LocalDateTime.now()) ?
            asyncPaymentsService.payments(order) : paymentsService.payements(order);
}
public PaymentResult payment(Order order, LocalDate targetDt) {
    return isPeperoDay(targetDt) ?
            asyncPaymentsService.payments(order) : paymentsService.payements(order);
}

 

 

테스트하기 어려운 것 - private method

private void order(User user, long bookId) {
    Book book = findBook(bookId);
    UserInfo userInfo = findUserInfo(user);
    BookStock bookStock = findBookStock(bookId);
    
    chackOnSale(book);
    checkDormant(userInfo);
    checkEnoughAge(book, userInfo);
    checkEnoughStock(bookStock);
    
    bookStock.decreaseStock();
    
    bookStockRepository.save(bookStock);
    bookOrderRepository.save(createBookOrder(user, book));
}

 

Q. private 메소드는 어떻게 테스트 해야하나요?

어떻게든 private 메소드를 테스트하고 싶다면, 그 메소드는 결코 private이면 안된다.

public으로 바꿔도 될지 마음에 걸린다면, 이 메소드는 별도의 책임의 일부로서 원래는 다른 클래스에 들어있어야 한다.

- 마이클 C. 페더스 - Working effectivel\y with legacy code 중

 

 

테스트하기 어려운 것 - 확률

Random random = new Random();
  • Random은 통계를 기반으로한 확률로 테스트할 수밖에 없음

 

 

테스트하기 어려운 것 - 자동 생성되는 코드들

  • Swager-autogen을 통해 만들어진 클래스들
  • QueryDSL을 사용하기 위해 만들어진 Qclass
  • Lombok으로 자동 생성되는 코드들 (Bytecode Instrumentation)

 

 

 

정리

- 테스트 코드의 딜레마

- 테스트 대상 (상태 vs 행위)

- 성공 테스트와 실패 테스트

- 테스트하기 어려운 요소들

  • 시간
  • private method
  • 확률
  • 자동 생성 코드들

 

 


반응형

+ Recent posts