테스트 주도 개발 시작하기 리뷰 - 4~6장
테스트 주도 개발 시작하기 리뷰 - 4~6장
들어가며
이 포스트는 최범균의 「테스트 주도 개발 시작하기」 3장을 읽고 개인적으로 학습한 내용을 정리한 글입니다.
- 책: 테스트 주도 개발 시작하기
- 저자: 최범균
- 출판사: 가메출판사
- 챕터: 4~6장
핵심 개념 요약
4장 TDD-기능 명세,설계
기능 명세
- 사용자에게 제공할 기능을 구현하려면 기능을 두 가지로 구분 할 수 있음
- 입력 : 입력값 (ex. id/pw)
- 결과 : 결과값 -> 성공 - 실패 - 변경
설계 과정을 지원하는 TDD
- TDD는 테스트를 먼저 만듦
- 테스트 코드를 만들기 위해 필요한 것
- 테스트할 기능을 실행 : 기능이 없으면 실행할 수 없음 => 테스트에서 실행할 수 있는 객체나 함수가 있어야함
- 실행 결과를 검증 : 테스트는 기능을 올바르게 동작하는지 검증하는 것이므로 실행 결과를 검증
필요한 만큼 설계하기
- TDD는 테스트를 통과할 만큼만 코드를 작성
- 이를 설계에 적용해 실제 테스트 사례를 추가하고 통과시키는 과정에서 필요한 만큼 설계를 변경
- TDD로 개발을 진행하면 현시점에서 테스트를 통과시키는 데 필요한 만큼의 코드만 만들게 된다
기능 명세 구체화
- 테스트 코드는 구체적인 입력과 결과를 이용해 작성하므로 개발자는 예를 통해 기능 명세를 구체화하게 된다
- 모호한 상황을 만나면 이를 구체적인 예로 바꾸어 테스트 코드에 반영 -> 테스트 코드는 예를 이용해 구체적인 명세가 된다
- 테스트 코드는 바로 실행할 수 있다. 테스트 코드를 이용하면 구체적인 예를 이용해 기능을 바로 실행해 볼 수 있다
- 복잡한 로직을 구현해야 하는 것은 결국 개발자이므로 개발자는 최대한 예외적인 상황이나 복잡한 상황에 해당하는 구체적인 예를 끄집어내야 한다
5장 JUnit 5 기초
JUnit 5 모듈 구성
- JUnit 5의 구성 요소
- JUnit 플랫폼 : 테스팅 프레임워크를 구동하기 위한 런처와 테스트 엔진을 위한 API를 제공
- JUnit Jupiter : JUnit 5를 위한 테스트 API와 실행 엔진을 제공한다
- JUnit Vintage : JUnit 3,4로 작성된 테스트를 JUnit 5 플랫폼에서 실행하기 위한 모듈을 제공
@Test 애노테이션과 테스트 메서드
- Test 어노테이션
1
2
3
4
5
6
7
8
9
10
11
12
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class SumTest {
@Test
void sum(){
int result = 2 + 3;
assertEquals(5, result);
}
}
주요 단언 메서드
| 메서드 | 설명 |
|---|---|
| assertEquals(expected,actual) | 실제 값이 기대하는 값과 같은지 검사 |
| assertNotEquals(expected,actual) | 실제 값이 특정 값과 같지 않은지 검사 |
| assertSame(Object expected, Object actual) | 두 객체가 동일한 객체인지 검사 |
| assertNotSame(Object umexpected, Object actual) | 두 객체가 동일하지 않은 객체인지 검사 |
| assertTrue(boolean condition) | 값이 true인지 검사 |
| assertFalse(boolean condition) | 값이 false인지 검사 |
| assertNull(Object actual) | 값이 null인지 검사 |
| assertNotNull(Object actual) | 값이 null이 아닌지 검사 |
| fail() | 테스트를 실패 처리 |
Assertions가 제공하는 익셉션 발생 유무 검사 메소드
| 메서드 | 설명 |
|---|---|
| assertThrows(Class<T> expectedType, Executable executable) | executable 실행 시 expectedType 익셉션이 발생하는지 검사 |
| assertDoesNotThrow(Executable executable) | executable 실행 시 익셉션이 발생하지 않는지 검사 |
테스트 라이프사이클
@BeforeEach 애노테이션과 @AfterEach 애노테이션
- Junit의 테스트 코드 실행 순서
- 테스트 메서드를 포함한 객체 생성
- (존재하면) @BeforeEach 애노테이션이 붙은 메서드 실행
- @Test 애노테이션이 붙은 메서드 실행
- (존재하면) @AfterEach 애노테이션이 붙은 메서드 실행
- @BeforeEach 애노테이션
- 테스트를 실행하는데 필요한 준비 작업을 할 때 사용
- 테스트에서 사용할 임시 파일을 생성하거나 테스트 메서드에서 사용할 객체를 생성
- @AfterEach 애노테이션
- 테스트를 실행한 후에 정리할 것이 있을 때 사용
- 테스트에서 사용한 임시 파일을 삭제해야 할 때 사용
@BeforeAll 애노테이션과 @AfterAll 애노테이션
- @BeforeAll 애노테이션
- 한 클래스의 모든 테스트 메서드가 실행되기 전에 특정 작업 수행
- 이 메서드는 클래스의 모든 테스트 메서드를 실행하기 전에 한 번 실행
- @AfterAll 애노테이션
- 클래스의 모든 테스트 메서드를 실행한 뒤에 실행
테스트 메서드 간 실행 순서 의존과 필드 공유하지 않기
- 코드 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class BadTest {
private FileOperator op = new FileOperator();
private static File file;
@Test
void fileCreationTest(){
File createdFile = op.createFile();
assertTrue(createdFile.length() > 0);
this.file = createdFile;
}
@TEst
void readFileTest(){
long data = op.readData(file);
assertTrue(data > 0);
}
}
- 이 테스트는 fileCreationTest() 메서드가 readFileTest() 메서드보다 먼저 실행된다는 것을 가정
- 하지만 테스트 메서드가 특정 순서대로 실행된다는 가정하에 테스트 메서드를 작성하면 안된다
- Junit이 테스트 순서를 결정하긴 하지만, 그 순서는 버전에 따라 달라져 readFileTest()가 먼저 실행되면 file 필드가 null이라 테스트가 실패한다
- 테스트 메서드는 서로 독립적으로 동작해야 한다.
추가 애노테이션: @DisplayName, @Disabled
- @DisplayName 애노테이션
- @DisplayName 애노테이션을 사용해 테스트에 표시 이름을 붙일 수 있다
- @Disabled 애노테이션
- 특정 테스트를 실행하지 않고 싶을 때 사용
모든 테스트 실행하기
- 모든 테스트를 실행하는 방법
- mvn test
- gradle test
6장 테스트 코드의 작성
테스트 코드의 구성 요소 : 상황, 실행, 결과 확인
- 기능은 상황에 따라 결과가 달라진다
- 테스트의 구성 요소
- 상황, 실행, 결과
- 어떤 상황이 주어지고, 그 상황에서 기능을 실행하고, 실행한 결과를 확인하는 세 가지가 테스트 코드의 기본 골격
- 상황-실행-결과 확인 구조에 너무 집착하지는 말자. 이 구조가 테스트 코드를 작성하는 데 도움이 되는 것은 맞지만 꼭 모든 테스트 메서드를 이 구조로 만들어야 하는 것은 아니다
외부 상황과 외부 결과
- 항상 테스트에 성공할 것이라는 보장은 없다
외부 상태가 테스트 결과에 영향을 주지 않게 하기
- TDD를 진행하는 동안에도 테스트 코드는 계속 실행하고 개발이 끝난 이후에도 반복적으로 테스트를 실행해서 문제가 없는지 검증 => 테스트는 언제 실행해도 항상 정상적으로 동작하는 것이 중요
- 간헐적으로 실패하거나 다른 테스트 다음에 실행해야 송공하면 테스트 결과를 믿을 수 없게 된다
외부 상태와 테스트 어려움
- 테스트 대상이 아닌 외부 요인은 테스트 코드에서 다루기 힘든 존재
- 외부 상황은 테스트 코드에서 마음대로 제어할 수 없는 경우가 있다
- 테스트 코드에서 생성한 외부 결과를 마음대로 초기화하기 힘들 때도 있다
- 테스트 대상의 상황과 결과에 외부 요인이 관여할 경우 대역을 사용하면 테스트 작성이 쉬워진다
This post is licensed under CC BY 4.0 by the author.