본문 바로가기

Server Development/Testing

UNIT TEST - JUNIT5, Mockito

 

 

Mockito

- 개발자가 동작을 직접 설정하는 방식으로 가짜 객체를 만들어주는 테스트 프레임 워크

- 가짜 객체를 만들어 Spring내 객체간의 의존성 없이 테스팅이 가능하게 만들어준다.

- 가짜 객체로 시뮬레이션(Stub)하여 단위 테스트를 진행하는 원리

 

 

관련 애너테이션

- @Mock : 가짜 객체를 만들어 줌.

- @Spy : 시뮬레이션을 만들지 않은 메서드 사용시 해당 클래스의 실제 메서드를 그대로 사용하게 해줌

- @InjectMocks : @Mock, @Spy 둘 중 하나로 생성된 가짜 객체를 자동 주입.

 

 

 

JUNIT5 + Mockto 

- 둘을 같이 쓰기 위해서는 결합이 필요하다.

- 아래 내용을 클래스 상단에 작성

@ExtendWith(MockitoExtension.class)

 

Mock객체에 필요한 필드 추가

- 간단한 변수들은 메서드를 만들어 삽입시킨다.

- 다른 객체들은 @Mock 처리시 삽입 가능하다.

 

 

 

(+) @WebMvcTest

추가적으로 다른 방법으로는 @WebMvcTest를 사용하는 방법도 존재한다.

이는 실제 컨트롤러 빈을 불러와 사용하는 방식이다.

@SpringBootTest와의 차이점은 SpringBootTest 애너테이션은 실제 모든 빈을 불러와 실제 환경과 동일하게 테스팅함을 의미하지만

@WebMvcTest는 설정한 컨트롤러 빈만 불러온다.

 

하지만 Spring Security Config 또한 불러오지 않기 때문에 Spring Test 시에 기본 Spring Security가 설정된다. 즉, Config에서 인증/인가 설정한 것이 적용되지 않는다. 따라서 csrf나 권한부여등을 테스트에서 따로 해주어야 한다.

 

  • .with(csrf()) 사용
  • @WithMockUser, @WithUserDetails, @WithAnonymousUser, with(user()) 등 사용해서 권한 부여

 

 

 

UNIT TEST의 과정 (BDD 채택)

1. given : 해당 메서드가 특정 값을 반환할 것임을 개발자가 설정

 

- 예시

- doReturn : 이러한 결과를 반한하게 설정할 것이다.

- when : 이러한 상황이 주어졌을 때

// given
MemberDTO sample = MemberDTO.sample();
String response = "success";
doReturn(response).when(memberServiceImpl).registerMember(any(MemberDTO.class));

(+)

doNothing() : 아무것도 안할 것이다. 

doThrow() : 예외를 발생기킬것이다.

 

(+) 반환 클래스의 입력시 any 사용

any(클래스명.class)

anyString()

anyInt()

 

 

 

(+) QueryFactory

- 순서대로 반환결과를 모두 Mocking 해주어야한다.

- 또한, FetchFirst()와 같은 메서드는 원래 두개의 메서드로 이루어져있다 limit+fetchOne. 따라서 둘다 반환을 정해주어야 한다.

예시 

Integer exist_id = queryFactory.selectOne().from(memberEntity)
        .where(memberEntity.userId.eq(user_id)).fetchFirst();

doReturn(jpaQuery).when(queryFactory).selectOne();
doReturn(jpaQuery).when(jpaQuery).from(memberEntity);
doReturn(jpaQuery).when(jpaQuery).where(memberEntity.userId.eq(userId));
doReturn(jpaQuery).when(jpaQuery).limit(1);
doReturn(entity).when(jpaQuery).fetchOne();

 

 

 

 

 

2. when : 상황을 부여

 

MockMvc

예시1 : Http 요청에 대한 Controller의 응답을 확인하는 경우

- ResultActions.class : 해당 동작에 대한 결과를 담는 클래스

- MockMvc.class : Spring에서 제공하는 HTTP 테스트 클래스

- .perform : 결과로는 ResultActions 클래스를 받으며, 요청을 전송하는 역할을 수행

- .post(), .get(), .put(), .delete() : HTTP 메서드를 의미

- .contentType : JSON 등 타입을 설정 가능

- .content : Body에 데이터를 담을 수 있다. 직접작성시 아래와 같이 작성

- .with() : 무언가 같이 붙여 전송가능 예를들어, csrf, user 등

- .param, .params : 파라미터 한개 또는 여러개 전송 가능, MultiValueMap클래스를 전달

예시 : .param("key", "value") -> int는 String으로 변환해 작성(1 -> "1")

"{\"userPwd\": \"userPwd\"}";
private MockMvc mockMvc;

// 해당 부분은 가짜 객체에 필요한 부분을 미리 세팅하는 것이 좋다.
@BeforeEach
public void init() {
    mockMvc = MockMvcBuilders.standaloneSetup(memberController).build();
}

// when
ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.post("/member")
    .contentType(MediaType.APPLICATION_JSON).content(new Gson().toJson(sample)));

 

(+) params 예시

MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
// 이 후, put메서드를 통해 String과 List<String>을 넣어준다.

 

 

 

 

3. then : 예상한 결과에 부합하는지 확인

 

- 예시 : 응답 결과 예상 및 결과 출력

- andExpect : status(), view(), redirect(), model(), content() 등 사용 가능

- andDo() : 결과 출력

- assertThat : 결과값 확인

- assertThrows(Exception클래스, () -> {클래스.메서드}) : 오류 처리

- assertEquals(String, String) : 둘의 내용이 같은지 확인

// then
resultActions.andExpect(status().isOk())
    .andExpect(MockMvcResultMatchers.content().string(equalTo(response))).andDo(print());
    
// json 값 확인
.andExpect(jsonPath("$.store1").value(1));

 

(+) 타입별 비교

// 1. String
assertEquals("A", "A");

// 2. Integer
assertThat(1, is(1));

// 3. Long
assertThat(1L, equalTo(1L));

// Null 체크
assertThat(1, is(nullValue()));

// 둘의 import가 다름
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;

 

 

 

 

4. verify : 받은 데이터 검증 과정

 

- 예시 : 가짜 객체로 만든 클래스의 메서드의 호출횟수 검증

// 해당 메서드가 한번 호출 되었는지
verify(memberDAOImpl, times(1)).registerMember(any(MemberEntity.class));


// 메서드가 조건에 맞는 인자로 호출되는지 검증
verify(storeListCache).save(argThat(storeListDTO ->
	storeListDTO.getAddress().equals(address) &&
	storeListDTO.getStoreList().equals(mockStoreList)));

verify(storeDAO).getStoreList(eq(country), eq(city), eq(dong), eq(type), eq(page), eq(size));

 

 

 

 

(+) DataBase Test

데이터 베이스를 테스트 하기 위한 기본적인 베이스는 아래와 같다.

1. 실제 데이터 베이스 연결 (H2등의 인메모리 또는 실제 사용하는 DB)

2. 필요한 Config 주입

 

 

- 기본적으로 @DataJpaTest는 인메모리 데이터베이스인 H2를 활용해 데이터를 테스팅한다. 이때 자동으로 롤백해주어 데이터 저장 및 삭제에 대해서 롤백처리를 해준다.

- @AutoConfigureTestDatebase는 자신이 사용하는 데이터 베이스를 사용할것인지 또는 설정을 추가할 것인지에 대해 설정을 해줄 수 있다. 아래와 같이 작성시 내가 사용하는 DB를 사용하겠다는 의미이다.

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)

 

 

추가적으로 H2 사용하는 방법을 적어보려고 한다.

1. 의존성 추가

2. test properties 생성 및 설정

3. @ActiveProfiles("test")를 테스트 코드에 작성

 

 

 

'Server Development > Testing' 카테고리의 다른 글

Integration Test  (0) 2023.08.04
Apache Jmeter  (0) 2023.05.15
Test Coverage with Jacoco  (0) 2023.04.02
Unit Test with MVC  (0) 2023.04.02
Swagger  (0) 2023.04.01