코딩하는 오징어

Spring test 와 Junit4를 이용한 테스트 본문

Framework/Spring

Spring test 와 Junit4를 이용한 테스트

코딩하는 오징어 2018. 8. 24. 23:53
반응형

Junit 4 & Spring Test을 이용한 TDD 환경 세팅

(다음은 Spring boot를 이용하지 않은 애플리케이션에서의 테스트 환경이다.)

  • SpringTestSupport 클래스에 설정 후 이 클래스를 상속받아 테스트를 개발함.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { RootContextConfig.class },
        loader = AnnotationConfigWebContextLoader.class)
@WebAppConfiguration
public class SpringTestSupport {

    @Autowired
    protected WebApplicationContext wac;

    protected MockMvc mockMvc;

    @Before
    public void setup() throws Exception {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }

    @Test
    public void isWebAppContextLoadingProperly() {
        ServletContext servletContext = wac.getServletContext();

        Assert.assertNotNull(servletContext);
        Assert.assertTrue(servletContext instanceof MockServletContext);
        Assert.assertNotNull(wac.getBean("rootContextConfig"));
    }

}

@RunWith(SpringJUnit4ClassRunner.class)

  • 이 애노테이션을 붙여줘야 스프링 테스트를 Junit으로 돌릴 수 있음.

@ContextConfiguration(classes = { RootContextConfig.class }, loader = AnnotationConfigWebContextLoader.class)

  • RootContextConfig.class를 spring context의 빈 설정 파일로 사용한다는 의미.

@WebAppConfiguration

  • 이 애너테이션을 붙이면 Controller및 web환경에 사용되는 빈들을 자동으로 생성하여 등록하게됨.

참고

  • DB 프로퍼티 파일은 src/test/resource경로가 classpath로 잡히기 때문에 이 경로에 db.properties파일이 있어야 함.(dp.properties파일 이름은 src/main/resource 경로에 있는 파일명과 동일하게 지었음. 따로 테스트전용 리소스 파일을 관리하지않고 h2database만 설정하기 위함)

Controller 테스트 예제

public class UserControllerTest extends SpringTestSupport {

    @Test
    public void issueTokenApiTest() throws Exception {
        MvcResult mvcResult = this.mockMvc.perform(MockMvcRequestBuilders.get("/v1/issue-token"))
                .andDo(MockMvcResultHandlers.print())
                .andExpect(MockMvcResultMatchers.status().is(HttpStatus.OK.value()))
                .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
                .andReturn();

        Assert.assertEquals(MediaType.APPLICATION_JSON_UTF8_VALUE,
                mvcResult.getResponse()
                        .getContentType());

        Assert.assertEquals(HttpStatus.OK.value(),
                mvcResult.getResponse()
                        .getStatus());
    }

}

h2database 설정 파일

# db connect setting
datasource.driverClassName=org.h2.Driver
datasource.url=jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_DELAY=-1;
datasource.username=codingsquid
datasource.password=1234

Bean Mocking Test

  • Controller를 테스트 할 경우 Service layer까지 내려가게됨. Controller만 테스트하고 싶다면 Service를 Mocking하면 됨.
  • mocktio framework을 이용하여 적용해보자.

import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

위와같은 import static을 통해 static method를 사용하게 하였다.

public class UserControllerTest extends SpringTestSupport {

    @Mock
    private UserService userService;

    @InjectMocks
    private UserController userController;

    @Test
    public void issueTokenApiIntegrateTest() throws Exception {
        this.mockMvc.perform(get("/v1/issue-token"))
                .andDo(print())
                .andExpect(status().is(HttpStatus.OK.value()))
                .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
                .andExpect(jsonPath("$.result.token").exists())
                .andExpect(jsonPath("$.result.created_at").exists());
    }

    @Test
    public void issueTokenApiUnitTest() {
        MockitoAnnotations.initMocks(this);
        LocalDateTime testTime = LocalDateTime.now();
        givenUserService(testTime);

        ApiResponseModel<AccessToken> result = userController.issueToken();

        Assert.assertEquals(result.getCode(), HttpStatus.OK.value());
        Assert.assertEquals(result.getMsg(), HttpStatus.OK.getReasonPhrase());
        Assert.assertEquals(result.getResult().getToken(), "this is sample access-token");
        Assert.assertEquals(result.getResult().getCreatedAt(), testTime);
    }

    private void givenUserService(LocalDateTime createdAt) {
        User mockUser = new User();
        mockUser.setAccessToken(new AccessToken("this is sample access-token", createdAt));
        when(userService.registerUser()).thenReturn(mockUser);
    }

}
  • Mocking전의 테스트 코드와 비교하여 달라진 점은 @Mock, @InjectMocks와 givenUserService 메서드가 추가돼고 MockitoAnnotations.initMocks(this); 줄이 포함됨. 하나씩 알아가 보자.
    • @Mock -> Mocking할 빈을 설정 한다.
    • @InjectMocks -> UserController빈을 Context에 등록할 때 연관관계를 가진 빈 대신 Mocking된 빈을 집어 넣는다.
    • givenUserService에서 when 메서드에 userService의 registerUser()메서드가 호출되면 mockUser를 return하게 만든다.
      • given, when, then 패턴을 알아두자.
    • MockitoAnnotations.initMocks(this); -> 현재 테스트 클래스의 @Mock이 달린 객체를 초기화하는 작업이다. 첨언하자면 테스트 클래스는 @Test가 붙은 메서드마다 객체가 새로 만들어지게 되며 issueTokenApiUnitTest메서드가 호출될 때 새로 생성된 테스트 클래스의 객체를 파라미터로 넣어준다.


반응형
Comments