코딩하는 오징어
Spring boot starter test 와 Junit5를 이용한 테스트 본문
반응형
Junit 5 & Spring Test을 이용한 TDD 환경 세팅
기본 세팅
@ExtendWith(SpringExtension.class) @SpringBootTest(classes = KkApplication.class) @ActiveProfiles("test") public abstract class SpringTestSupport { }
- TestContext를 사용하려면 위의 SpringTestSupport를 상속받아 테스트 코드를 개발한다.
- @ExtendWith는 Junit4의 RunWith(SpringRunner.class)와 비슷하다고 생각하면 된다.
- @SpringBootTest는 @SpringBootApplication이 붙은 애너테이션을 찾아 context를 찾는다.
- @SpringBootApplication을 사용하지 않고 다음과 같이 애너테이션을 풀어 개발했다면 위와 같이 classes필드를 통해 지정해야한다.
@Configuration @EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class }) @ComponentScan( basePackageClasses = KkApplication.class, excludeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public class KkApplication { public static void main(String[] args) { SpringApplication.run(KkApplication.class, args); } }
MockMvc 테스트 세팅
@AutoConfigureMockMvc public abstract class SpringMockMvcTestSupport extends SpringTestSupport { @Autowired protected MockMvc mockMvc; }
- @AutoConfigureMockMvc를 통해 MockMvc를 Builder없이 주입 받을 수 있다. 스프링부트의 매력인 Auto Config의 장점이다. 이 애너테이션을 상속받아 controller 테스트들을 진행한다. 다음은 샘플 테스트 코드이다.
class HealthCheckerControllerTest extends SpringMockMvcTestSupport { @Test void healthCheckTest() throws Exception { this.mockMvc.perform(get("/v1/health-checker")) .andDo(print()) .andExpect(status().is(HttpStatus.OK.value())) .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) .andExpect(jsonPath("$.deployDate").exists()) .andExpect(jsonPath("$.deployVersion").exists()) .andExpect(jsonPath("$.distributor").exists()); } }
Mock 테스트 세팅
public class MockitoExtension implements TestInstancePostProcessor, ParameterResolver { @Override public void postProcessTestInstance(Object testInstance, ExtensionContext context) { MockitoAnnotations.initMocks(testInstance); } @Override public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { return parameterContext.getParameter().isAnnotationPresent(Mock.class); } @Override public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { return getMock(parameterContext.getParameter(), extensionContext); } private Object getMock(Parameter parameter, ExtensionContext extensionContext) { Class<?> mockType = parameter.getType(); Store mocks = extensionContext.getStore(Namespace.create(MockitoExtension.class, mockType)); String mockName = getMockName(parameter); if (mockName != null) { return mocks.getOrComputeIfAbsent(mockName, key -> mock(mockType, mockName)); } else { return mocks.getOrComputeIfAbsent(mockType.getCanonicalName(), key -> mock(mockType)); } } private String getMockName(Parameter parameter) { String explicitMockName = parameter.getAnnotation(Mock.class).name().trim(); if (!explicitMockName.isEmpty()) { return explicitMockName; } else if (parameter.isNamePresent()) { return parameter.getName(); } return null; } }
- 위의 MockitoExtension 클래스를 @ExtendWith의 value 필드에 넣음으로써 Mock테스트에 필요한 곳에 애너테이션을 달면 된다. 다음은 샘플 코드이다.
import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class HealthCheckerControllerTest extends SpringMockMvcTestSupport { @Mock private BookRepository bookRepository; @InjectMocks private BookService bookService; private givenBookRepository() { when(bookRepository.findByXXX(..)).thenReturn(...); } @Test void healthCheckTest() throws Exception { this.mockMvc.perform(get("/v1/health-checker")) .andDo(print()) .andExpect(status().is(HttpStatus.OK.value())) .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) .andExpect(jsonPath("$.deployDate").exists()) .andExpect(jsonPath("$.deployVersion").exists()) .andExpect(jsonPath("$.distributor").exists()); } }
- 여기서 @Test메서드에 @Mock 파라미터로 바로 줄 수도 있다. @Test메서드에 파라미터를 줄 수 있는 것은 Junit4와 달라진 Junit5의 장점인 것 같다.
주의
- org.junit.jupiter.api.Test를 import하여 @Test를 이용하여야 @ExtendWith(SpringExtension.class)를 통해 컨텍스트를 끌어 올 수 있다. null pointer exception이 발생한다면 org.junit.test가 import 되어있는지 확인해보자.
- build.gradle에 다음과 같이 설정을 해야 @Test가 달린 메서드들을 테스트한다. gradle 버전이 4.6이상이어야 함을 참고하자.
build.gradle
dependencies { ... testCompile('org.springframework.boot:spring-boot-starter-test') testCompile("org.junit.platform:junit-platform-launcher:1.1.1") testCompile("org.junit.platform:junit-platform-suite-api:1.1.1") testCompile("org.junit.jupiter:junit-jupiter-engine:5.1.1") testCompile("org.junit.vintage:junit-vintage-engine:5.1.1") ... } test { useJUnitPlatform() //Gradle 4.6이상에서 사용할 수 있는 설정 systemProperty 'spring.profiles.active', 'test' }
/gradle/wrapper/gradle-wrapper.properties
...
distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-all.zip
//필자는 Junit5로 테스트를 짰지만 gradle test task로 테스트들이 실행되지 않음을 발견하였다.
//4.5.1 -> 4.8.1로 올려서 task를 실행시켜주었더니 필자가 겪은 에러가 해결되었다.
반응형
'Framework > Spring' 카테고리의 다른 글
Spring boot profile 적용하기 (1) | 2018.08.25 |
---|---|
Spring test 와 Junit4를 이용한 테스트 (0) | 2018.08.24 |
Spring & JPA의 Hibernate 설정시 주의 사항 & CandidateComponentsIndexLoader 클래스 (0) | 2018.07.28 |
Spring web mvc & jpa - hibernate 빈 설정 (MAVEN) (0) | 2018.07.15 |
자동인식을 이용한 빈등록: 스테레오타입 애노테이션과 빈스캐너 (0) | 2018.03.28 |
Comments