given - when - then
expected
)과 테스트 대상 메서드의 동작 수행 결과(actual
) 값을 비교해서 기대한대로 동작을 수행하는지 검증(Assertion)하는 코드들이 포함Assertion(어써션)은 ‘예상하는 결과 값이 참(true)이길 바라는 것
@Test
애너테이션을 추가@DisplayName("Hello JUnit Test")
: 실행결과 창에 표시되는 이름 설정() →
테스트 대상 메서드) : 예외(Exception) 테스트@BeforeEach
와 @BeforeAll()
@AfterEach
, @AfterAll
assumeTrue()
메서드는 파라미터로 입력된 값이 true
이면 나머지 아래 로직들을 실행//JUnit Assertion
assertEquals(expected, actual)
assertNotNull(currencyName, "should be not null");
assertThrows(NullPointerException.class, () -> getCryptoCurrency("XRP"));
// Hamcrest Assertion 하나의 영문장처럼 표현가능 -> 가독성 up;
assertThat(actual, is(equalTo(expected)));
assertThat(currencyName, is(notNullValue()));
// Hamcrest 만으로 Assertion을 구성하기 힘들기 때문에 JUnit의 assertThrows() 메서드를
//이용해서 assertThrows()의 리턴 값으로 전달 받은 Exception 내부의 정보를 가져와서
Throwable actualException = assertThrows(NullPointerException.class,
() -> getCryptoCurrency("XRP"));
// 아래처럼 추가로 검증해야 한다.
assertThat(actualException.getCause(), is(equalTo(null)));
@SpringBootTest
// Spring Boot 기반의 애플리케이션을 테스트 하기 위한 Application Context를 생성
//애플리케이션에 필요한 Bean 객체들이 등록되어 있다.
@AutoConfigureMockMvc
// Controller 테스트를 위한 애플리케이션의 자동 구성 작업을 해준다.
public class ControllerTestDefaultStructure {
// DI로 주입바은 MockMvc는 Tomcat 서버 실행 없이 테스트할 수 있는 완벽한 환경을 지원
// 해주는 일종의 Spring MVC 테스트 프레임워크이다. MockMvc 객체를 통해 우리가 작성한 Controller를 호출
@Autowired
private MockMvc mockMvc;
// (4)
@Test
public void postMemberTest() {
// given (5) 테스트용 request body 생성
// when (6) MockMvc 객체로 요청 URI, HTTP 메서드 지정하고, 5에서 만든
// requset body추가하여 request 진행
ResultActions actions =
mockMvc.perform(//Controller의 핸들러 메서드에 요청을 전송하기
//perform 메서드 호출
//MockMvcRequestBuilders 클래스를 이용해서 빌더 패턴을 통해 request 정보 채워넣음
post("/v11/members")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(content) // request Body 데이터 설정
);
// then Controller 핸들러 메서드에서 응답으로 수신한 HTTP Status 및 response body 검증
//ResultActions 객체를 이용해서 우리가 전송한 request에 대한 검증을 수행
MvcResult result = actions
//andExpect() 메서드를 통해 파라미터로 입력한 매처(Matcher)로 예상되는 기대 결과를 검증
.andExpect(status().isCreated())
//jsonPath()를 사용하면 JSON 형식의 개별 프로퍼티에 손쉽게 접근
andExpect(jsonPath("$.data.email").value(post.getEmail()))
//andReturn()을 통해서 response 데이터를 확인 디버깅 용도
.andReturn();
// System.out.println(result.getResponse().getContentAsString());
}
}
//response body로 전달된 JSON 데이터에서 한글이 깨져 보일 경우, application.yml 파일에 아래의 설정을 추가
server:
servlet:
encoding:
force-response: true
@WebMvcTest
@DataJpaTest
애너테이션을 테스트클래스에 추가하면, Repository 기능을 사용하기 위한 설정 Spring이 자동으로 해준다.@Transactional
애너테이션을 포함하고 있다.@SpringBootTest
@AutoConfigureMockMvc
class MemberControllerMockTest {
...
// Application Context에 등록되어 있는 Bean에 대한 Mock객체를 생성하고 주입해주는 역할
@MockBean
private MemberService memberService;
@Autowired
private MemberMapper mapper;
@Test
void postMemberTest() throws Exception {
// given
MemberDto.Post post = new MemberDto.Post("hgd@gmail.com",
"홍길동",
"010-1234-5678");
Member member = mapper.memberPostToMember(post);
member.setStamp(new Stamp());
// Mockito에서 지원하는 Stubbing 메서드
// Mock 객체가 특정 값을 리턴하는 동작을 지정하는데 사용
given(memberService.createMember
(Mockito.any(Member.class))) // Mockito에서 지원하는 변수 타입 중 하나
// Mock 객체 MemberService의 createMember() 메서드가 리턴 할 Stub 데이터
.willReturn(member);
String content = gson.toJson(post);
// when
ResultActions actions =
mockMvc.perform(
post("/v11/members")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(content)
);
// then
MvcResult result = actions
.andExpect(status().isCreated())
.andExpect(jsonPath("$.data.email").value(post.getEmail()))
.andExpect(jsonPath("$.data.name").value(post.getName()))
.andExpect(jsonPath("$.data.phone").value(post.getPhone()))
.andReturn();
// System.out.println(result.getResponse().getContentAsString());
}
}
Stubbing
Mock 객체가 항상 일정한 동작을 하도록 지정하는 것
- Mock 객체인
MemberService
클래스는 우리가 테스트하고자 하는 Controller의 테스트에 집중할 수 있도록 다른 계층과의 연동을 끊어주는 역할을 한다.- Mockito를 잘 이용하면 의존하는 다른 메서드 호출이나 외부 서비스의 호출을 단절 시킬 수 있기 때문에 우리가 원하는 테스트의 범위를 최대한 좁힐 수 있다.
//Junit에서 Spring을 사용하지 않고 순수하게 Mockito의 기능만을 사용하기 위해서는
// @ExtendWith(MockitoExtension.class)를 추가해야 한다.
@ExtendWith(MockitoExtension.class)
public class MemberServiceMockTest {
@Mock // @Mock을 추가하면 해당 필드의 객체를 Mock객체로 생성
private MemberRepository memberRepository;
@InjectMocks // @Mock 애너테이션을 통해 생성된 Mock 객체는 @InjectMocks 애너테이션 을 추가한 필드에 주입된다.
private MemberService memberService;
@Test
public void createMemberTest() {
// given
Member member = new Member("hgd@gmail.com", "홍길동", "010-1111-1111");
//Mock객체로 Stubbing
given(memberRepository.findByEmail(Mockito.anyString()))
.willReturn(Optional.of(member)); // (5)
// when / then (6)
assertThrows(BusinessLogicException.class, () -> memberService.createMember(member));
}
}
failed
”인 테스트 케이스를 지속적으로 그리고 단계적으로 수정하면서 테스트 케이스 실행 결과가 “passed
”가 되도록 만들고 있습니다.passed
” 될 만큼의 코드만 우선 작성합니다.실패하는 테스트 → 실패하는 테스트를 성공할 만큼의 기능 구현 → 성공하는 테스트 → 리팩토링 → 실패하는 테스트와 성공하는 테스트 확인
’ 이라는 흐름을 반복합니다.