Test Driven Development
테스트 코드를 먼저 작성하고 그 테스트를 통과하는 실제 코드를 작성하는 개발 방법론이다.

Java를 위한 대표적인 단위 테스팅 프레임워크
SpringBoot 2.2 이상의 버전을 사용한다면 JUnit5 의존성이 추가되어있다!
최신버전은 JUnit5이며
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
라고 표현할 수 있다.
JUnit Jupiter: TestEngine API 구현체로, JUnit5를 구현하고 있음
JUnit Platform: Test를 실행하기 위한 뼈대로 테스트 코드 작성에 필요한 junit-jupiter-api 모둘과 테스트 ㅅ길행을 위한 junit-jupiter-engine 모듈로 분리되어 있다.
Junit Vintage: JUnit3, JUnit4를 실행할 수 있는 TestEngine으로 하위버전의 호화능ㄹ 위해서 존재
@Test
public void test(){
// 테스트 코드
}
@Test: 해당 메서드가 테스트 메서드임을 나타냄@ParametherizedTest: 해당 메서드가 매개변수가 있는 테스트임을 나타냄@RepeatedTest(n) : n만큼 반복되는 테스트 메서드임을 나타냄어노테이션을 이용해 메서드가 테스트 메서드임을 명시하고, Assertion 예외를 발생시키지 않으면 성공으로 간주한다.
@Test
@DisplayName("새로운 게시글 추가 시 글이 저장되고 201 상태코드가 반환된다.")
void createArticleTest() throws Exception {
}
@Test
@DisplayName("존재하지 않는 ID로 게시글 조회시 404 에러가 발생한다.")
void getArticleNotFoundTest(){
}
JUnit 테스트의 시작부터 종료까지의 과정으로 각 단계에서는 특정 작업이 수행 되며 이를 통해 테스트가 원할하게 진행된다.
테스트 클래스 인스턴스 새로 생성
생성한 클래스 인스턴스가 가지고 있는 테스트 환경준비(setup) 메서드를 찾아 모두 호출
setup 메서드는 각 테스트가 실행되기전 호출setup 메서드를 통해 테스트에 필요한 초기 설정을 할 수 있다.테스트 메서드 호출
테스트 클래스 인스턴스가 가지고 있는 테스트 환경정리(teardown) 메서드를 찾아 모두 호출
teardown 메서드는 각 테스트가 종료된 후에 호출
setup: 초기 환경 설정
teardown: 테스트 환경 정리
| 어노테이션 | 설명 |
|---|---|
@BeforeEach |
각 테스트 메서드가 실행되기 전 실행되는 메서드를 나타냄 |
@BeforeAll |
테스트 클래스의 시작 전에 한번 수행되는 메서드를 나타냄 |
@AfterEach |
각 테스트 메서드가 실행 후에 실행되는 메서드를 나타냄 |
@AfterAll |
테스트 클래스의 실행 후에 실행되는 메서드를 나타냄 |
public class LifeCycleTest {
@Test
void testMethod1(){
System.out.println("testMethod1 실행");
assertEquals(2, 1+ 1);
System.out.println("testMethod1 assertEquals 완료");
}
@Test
void testMethod2(){
System.out.println("testMethod2 실행");
assertEquals(7, 1 + 2);
System.out.println("testMethod2 assertEquals 완료");
}
@BeforeAll
static void setupBeforeAll(){
System.out.println("모든 테스트 메서드 실행 전");
}
@AfterAll
static void tearDownAfterAll(){
System.out.println("모든 테스트 메서드 실행 후");
}
@BeforeEach
void setupBeforeEach(){
System.out.println("각 테스트 메서드 실행 전");
}
@AfterEach
void tearDownAfterEach(){
System.out.println("각 테스트 메서드 실행 후");
}
}
| 메서드 | 설명 |
|---|---|
assertEquals(expected, actual) |
기대값(expected)과 실제값(actual)이 동일한지 확인 |
assertNotEquals(unexpected, actual) |
기대하지않은 값과 실제값(actual)이 틀린게 맞는지확인 |
assertTrue(condition) |
주어진 조건(condition) 이 true인지 확인 |
assertFalse(condition) |
주어진 조건(condition)이 false인지 확인 |
assertNull(object) |
주어진 객체가 null인지 확인 |
assertNotNull(Object) |
주어진 객체가 null이 아닌지 확인 |
assertSame(expected, actual) |
expected와 actual이 동일한 객체를 참조하는지 확인 |
assertNotSame(unexpected, actual) |
unexpected와 actual이 다른 객체를 참조하는지 확인 |
assertArrayEquals(expectedArray, actualArray)
|
두 배열이 동일한 순서와 값을 가지는지 확인 |
assertThrows(expectedException, executable) |
주어진 executable이 expectedException을 발생시키는지 확인 |
assertThat(T actual, Matcher<? Super T> matcher)
actual: 검증하려는 실제 값matcher: 실제 값에 적용되는 조건을 정의하는 객체주어진 실제 값과 Matcher 객체를 활용해 예상되는 조건을 비교한다.
Hamcrest 라이브러리를 사용해야하며 테스트 코드의 가독성을 향상시킬 수 있다.
String actualString = "Hello, World";
int actualVal = 5;
assertThat(actualString, is("Hello, World"));
assertThat(actualVal, is(5));
assertThat(actualString, startsWith("Hello"));
assertThat(actualString, endsWith("World"));
assertThat(actualVal, is(not(10)));
테스트의 구조를 명확하게 나타내기 위해 사용, 각각의 단계는 테스트의 준비, 실행 그리고 단계를 나타냄
@Test
public void testAddition(){
// given 테스트에 필요한 데이터나 상태를 준비
SimpleCalculator calculator = new SimpleCalculator();
int a = 2;
int b = 3;
// when 실제로 테스트하려는 동작을 수행
int res = calculator.subtract(a, b);
// then 결과 검증
assertEquals(5, res);
}
Mock의 사전적 의미는 가짜의, 모조품으로, 실제 객체를 만들기엔 비용가 시간이 많이 들거나 의존성이 길게 걸쳐져 있어 제대로된 구현이 어려울 경우, 가짜 객체를 사용하는데 이것을 Mock이라고 한다.
실제 객체와 동일한 모의 객체를 만들어 테스트의 효용성을 높이기 위해 사용한다.
Java를 위한 Mokcing 프레임워크
Mock 객체의 생성 및 관리를 도와준다
테스트의 격리를 유지하며 테스트 대상 코드와 의존성 사이의 상호작용을 검증 또는 특정 상황을 시뮬레이션 할 수 있다.
class BlogServiceTest {
@Mock
private BlogRepository blogRepository;
@InjectMocks
private BlogService blogService;
@BeforeEach void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
@DisplayName("블로그 글 업데이트 테스트")
void updateArticleTest() {
//given
Long articleId = 1L;
Article existingArticle = new Article("Old Title", "Old Content");
UpdateArticleRequest request = new UpdateArticleRequest("New Title", "New Content");
when(blogRepository.findById(articleId)).thenReturn(Optional.of(existingArticle));
//when
Article updatedArticle = blogService.update(articleId, request);
Article test = blogService.findById(articleId);
//then
assertNotNull(updatedArticle);
assertEquals(updatedArticle.getTitle(), "New Title");
assertEquals(updatedArticle.getContent(), "New Content");
assertEquals(test.getTitle(), "New Title");
verify(blogRepository, times(2)).findById(articleId);
}
}
@Mock 선언한 객체가 Mock 객체임을 의미
@InjectMocks Mock 객체를 선언한 객체에 주입한다는 것을 의미
when() Mock 객체가 수행할 메서드와 그에 따른 반환값을 지정해준다.
MVC 패턴을 레이어 별로 잘라서, 레이어를 하나의 단위로 보는 단위 테스트를 의미한다.
@ExtendWith(SpringExtension.class)
@WebMvcTest(GreetingController.class)
public class MockMVCTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testGreetWithoutName() throws Exception {
ResultActions res = mockMvc.perform(get("/greeting"));
res.andExpect(status().isOk())
.andExpect(content().string("Hello, World!"));
}
}
MockMvc: 클라이언트의 요청을 테스트할 컨트롤러로 전달하는 역할 수행get(), post(), put(), deleate() 등의 메서드로 요청을할 수 있다.ResultActions: andExpect() 메서드를 통해 컨트롤러의 결과를 검증