다루는 내용
- 통합 테스트
- 서버에서 애플리케이션을 테스트하는 방법
- 스프링 부트 테스트 유틸리티
★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆
6년 전 기술이므로 현재 용어가 많이 바뀌었다.
흐름만 파악하고 공식문서를 참고하여 개발하도록하자.
https://spring.io/guides/gs/testing-web/
★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆
통합 테스트를 위한 자동 구성
스프링 애플리케이션 컨텍스트를 로드하고 테스트 클래스에 자동으로 주입하는 기능을 확성화하는 JUnit 클래스 러너인 SpringJUnit4ClassRunner를 제공하는 형태로 통합 테스트를 지원한다.
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ReadingListApplication.class)
@WebAppConfiguration
public class ReadingListApplicationTests {
@Test
public void contextLoads() {
...
}
}
- @RunWith: SpringJUnit4ClassRunner를 전달하여 스프링 통합 테스트를 활성화한다.
- @ContextConfiguration: 애플리케이션 컨텍스트를 어떻게 로드할지 지정한다. 하지만 스프링 부트 기능을 완전하게 로드하지 못한다. @SpringApplicationConfiguration로 바꿔 사용하도록 하자.
웹 애플리케이션 테스트하기
1. 스프링 Mock MVC
애플리케이션 서버를 구동하지 않고도 서블릿 컨테이너와 거의 비슷하게 작동하는 목 구현체로 컨트롤러를 테스트할 수 있다.
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ReadingListApplication.class)
@WebAppConfiguration
public class MockMvcWebTests {
@Autowired
private WebApplicationContext webContext;
private MockMvc mockMvc; //MockMvc 인스턴스 생성
@Before
public void setupMockMvc() {
mockMvc = MockMvcBuilders // context주입
.webAppContextSetup(webContext)
.build();
}
@Test
public void homePage() throws Exception { // GET 테스트
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(view().name("readingList"))
.andExpect(model().attributeExists("books"))
.andExpect(model().attribute("books", is(empty())));
}
@Test
public void postBook() throws Exception { // POST 테스트
mockMvc.perform(post("/") // POST 요청 수행
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("title", "BOOK TITLE")
.param("author", "BOOK AUTHOR")
.param("isbn", "1234567890")
.param("description", "DESCRIPTION"))
.andExpect(status().is3xxRedirection())
.andExpect(header().string("Location", "/"));
Book expectedBook = new Book(); // 생성할 책 정보 설정
expectedBook.setId(1L);
expectedBook.setReader("craig");
expectedBook.setTitle("BOOK TITLE");
expectedBook.setAuthor("BOOK AUTHOR");
expectedBook.setIsbn("1234567890");
expectedBook.setDescription("DESCRIPTION");
mockMvc.perform(get("/")) // GET 요청을 실행해 새로 생성된 모델이 있는지 검증
.andExpect(status().isOk()) // 정적 임포트 선언 후
.andExpect(view().name("readingList"))
.andExpect(model().attributeExists("books"))
.andExpect(model().attribute("books", hasSize(1)))
.andExpect(model().attribute("books", contains(samePropertyValuesAs(expectedBook))));
}
}
Mock MVC를 설정하기 위해 MockMvcBuilders를 사용한다. 이 클래스는 정적 메서드 두 개를 제공한다.
- standaloneSetup() : 수동으로 생성하고 구성한 컨트롤러 한 개 이상을 서비스할 Mock MVC를 만든다.
- webAppContextSetup() : 컨트롤러 한 개 이상을 포함하는 스프링 애플리케이션 컨텍스트를 사용하여 Mock MVC를 만든다.
- @WebApplicationConfiguration: SpringJUnit4ClassRunner가 애플리케이션 컨텍스트로 기본값인 비웹용 ApplicationContext가 아니라 WebApplicationContext를 생성하도록 선언한다.
- @Before: 애너테이션을 붙여 다른 테스트 메서드보다 먼저 실행함을 나타낸다.
다음 명령으로 테스트를 수행한다.
$ gradle test
2. 웹 통합 테스트
톰캣, 제티 등 내장 서블릿 컨테이너에서 애플리케이션을 실행하여 실제 애플리케이션 서버에서 테스트할 수 있다.
스프링 시큐리티는 간단한 보안 웹 애플리케이션 테스트를 지원한다. 빌드에 스프링 시큐리티 테스트 모듈을 추가하자. 그레이들을 사용할 때는 testCompile을 추가하면 된다.(테스트 시에만 컴파일 된다는 의미)
testCompile 'org.springframework.security:spring-security-test'
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ReadingListApplication.class)
@WebAppConfiguration
public class MockMvcWebTests {
@Autowired
private WebApplicationContext webContext;
private MockMvc mockMvc;
@Before
public void setupMockMvc() {
mockMvc = MockMvcBuilders
.webAppContextSetup(webContext)
.apply(springSecurity())
.build();
}
@Test
public void homePage_unauthenticatedUser() throws Exception {
mockMvc.perform(get("/"))
.andExpect(status().is3xxRedirection())
.andExpect(header().string("Location", "http://localhost/login"));
}
@Test
@WithUserDetails("craig")
public void homePage_authenticatedUser() throws Exception {
Reader expectedReader = new Reader();
expectedReader.setUsername("craig");
expectedReader.setPassword("password");
expectedReader.setFullname("Craig Walls");
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(view().name("readingList"))
.andExpect(model().attribute("reader", samePropertyValuesAs(expectedReader)))
.andExpect(model().attribute("books", hasSize(0)))
.andExpect(model().attribute("amazonID", "habuma-20"));
}
}
springSecurity() 메서드는 Mock MVC용으로 스프링 시큐리티를 활성화하는 Mock MVC 구성자를 반환한다. 간단히 적용만 하면 MockMvc로 수행하는 모든 요청에 스프링 시큐리티가 적용된다.
@Test
@WithMockUser(username="craig", password="password", roles="READER")
public void test() throws Exception {
...
}
- @WithMockUser: 지정한 사용자 이름, 패스워드, 권한으로 UserDetails를 생성한 후 보안 컨텍스트를 로드한다.
- @WithUserDetails: 지정한 사용자 이름으로 UserDetails 객체를 조회하여 보안 컨텍스트를 로드한다.
테스트를 수행하려고 어떤 서블릿 컨테이너도 시작한 적이 없다. 스프링 Mock MVC가 실제 서블릿 컨테이너를 대신한다. 그렇지만 이 테스트는 완전하지 않다.
실행 중인 애플리케이션 테스트하기
테스트 클래스에 @SpringBootTest (Spring Boot 1.2.1 이상 적용) 애너테이션을 붙이면 스프링 부트가 테스트용 애플리케이션 컨텍스트를 생성하면서 내장 서블릿 컨테이너도 시작한다. 애플리케이션을 내장 컨테이너와 함께 실행하고나면 이를 대상으로 실제 HTTP 요청을 보낼 수 있고 결과를 검증할 수 있다.
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ReadingListApplication.class)
@SpringBootTest(randomPort = true)
public class SimpleWebTest {
@Value("${local.server.port}")
private int port;
@Test(expected = HttpClientErrorException.class)
public void pageNotFound() {
try {
RestTemplate rest = new RestTemplate();
rest.getForObject("http://localhost:{port}/bogusPage", String.class, port);
fail("Should result in HTTP 404");
} catch (HttpClientErrorException e) {
assertEquals(HttpStatus.NOT_FOUND, e.getStatusCode());
throw e;
}
}
}
테스트 방식이 지원하는 모듈에 따라 천차만별이고 자주 변화하므로 지속적인 공부가 필요하다...!
길벗 깃허브 : https://github.com/gilbutITbook/006859/tree/master/ch04
'Develop > Spring˙Spring Boot' 카테고리의 다른 글
[Spring Boot] 애플리케이션 배포하기 (0) | 2023.02.18 |
---|---|
[Spring Boot] 액추에이터로 내부 들여다보기 - 2 (0) | 2023.02.17 |
[Spring Boot] 액추에이터로 내부 들여다보기 - 1 (0) | 2023.02.07 |
[Spring Boot] 구성을 사용자화하기 (0) | 2023.02.03 |
[Spring Boot] 스프링 부트 애플리케이션 개발하기 (0) | 2023.01.26 |