-
스프링부트 웹레이어에서 테스트공부/자바 2023. 7. 27. 22:57728x90간단한 Spring 애플리케이션을 만들고 JUnit으로 테스트한다. Spring Test와 Spring Boot 기능을 사용하여 Spring과 코드 사이의 상호작용을 테스트한다. 먼저 애플리케이션 컨텍스트가 성공적으로 로드되는 간단한 테스트로 시작하고, 이어서 Spring의 MockMvc를 사용하여 웹 레이어만을 테스트한다.
Spring Initializr로 시작하기
https://start.spring.io 로 이동한다. 이 서비스는 애플리케이션에 필요한 모든 종속성을 가져오고 대부분의 설정을 자동으로 해준다.
Gradle 또는 Maven 중에서 선택하고 사용하려는 언어를 선택한다.
Dependencies를 클릭하고 Spring Web을 선택한다.
Generate를 클릭한다.
선택한 내용으로 구성된 웹 애플리케이션 압축 파일을 다운로드 한다.Create a simple application
package com.example.testingWeb; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class TestingWebApplication { public static void main(String[] args) { SpringApplication.run(TestingWebApplication.class, args); } }
Run the Application
package com.example.testingWeb; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class HomeController { @RequestMapping("/") public @ResponseBody String greeting() { return "Hello, World"; } }
@SpringBootApplication: 다음과 같은 모든 기능을 추가하는 편리한 어노테이션이다.
@Configuration: 이 클래스를 애플리케이션 컨텍스트의 빈 정의 소스로 태그한다.
@EnableAutoConfiguration: 클래스 패스 설정, 다른 빈들, 그리고 다양한 속성 설정에 기반하여 Spring Boot가 빈을 추가하기 시작하도록 지시한다.
@EnableWebMvc: 애플리케이션을 웹 애플리케이션으로 지정하고 DispatcherServlet을 설정하는 등의 주요 동작을 활성화한다. Spring Boot는 클래스 패스에서 spring-webmvc를 발견하면 자동으로 이 설정을 추가한다.
@ComponentScan: Spring에게 com.example.testingweb 패키지에서 다른 컴포넌트, 설정 및 서비스를 찾도록 지시하여 HomeController클래스를 찾을 수 있도록 한다.
main() 메서드는 Spring Boot의 SpringApplication.run() 메서드를 사용하여 .xml 파일없이 애플리케이션을 실행한다.
로그 출력이 표시된다. 서비스는 몇 초 내에 실행 준비가 완료된다.Test the Application
변경 사항이 있을 때 애플리케이션이 정상적으로 작동하는지 더 확신하기 위해 테스트를 자동화하고 싶다.
처음으로 할 수 있는 것은 애플리케이션 컨텍스트가 시작되지 않으면 실패하는 간단한 sanity check 테스트를 작성하는 것이다.package com.example.testingWeb; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class TestingWebApplication { public static void main(String[] args) { SpringApplication.run(TestingWebApplication.class, args); } }
@SpringBootTest 어노테이션은 Spring Boot에 설정 클래스(예: @SpringBootApplication 어노테이션이 있는 클래스)를 찾아서 Spring 애플리케이션 컨텍스트를 시작하도록 지시한다. 이 테스트는 IDE에서 실행하거나 커맨드 라인에서 실행할 수 있다. 컨텍스트가 컨트롤러를 생성하는지 확인하려면 다음 예제처럼 assertion을 추가해야 한다.
package com.example.testingWeb; import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class SmokeTest { @Autowired private HomeController controller; @Test public void contextLoads() throws Exception { assertThat(controller).isNotNull(); } }
Spring는 @Autowired 어노테이션을 해석하고, 테스트 메서드가 실행되기 전에 컨트롤러가 주입된다. 테스트 assertion을 표현하기 위해 AssertJ를 사용한다. AssertJ는 assertThat() 및 기타 메서드를 제공한다.
Spring Test 지원의 좋은 기능 중 하나는 애플리케이션 컨텍스트가 테스트 간에 캐시된다는 것이다. 따라서 하나의 테스트 케이스에 여러 메서드가 있거나 동일한 구성을 가진 여러 테스트 케이스가 있는 경우, 애플리케이션을 시작하는 비용이 단 한 번만 발생한다. @DirtiesContext 어노테이션을 사용하여 캐시를 제어할 수 있다.
유효성 검사를 수행하는 것도 좋지만, 애플리케이션의 동작을 단언하는 몇 가지 테스트도 작성해야 한다. 이를 위해, 애플리케이션을 시작하고 수신 연결을 하여 http 요청을 보내고 응답을 확인할 수 있다.package com.example.testingWeb; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.beans.factory.annotation.Value; import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) public class HttpRequestTest { @Value(value="${local.server.port}") private int port; @Autowired private TestRestTemplate restTemplate; @Test public void greetingShouldReturnDefaultMessage() throws Exception { assertThat(this.restTemplate.getForObject("http://localhost:" + port + "/", String.class)).contains("Hello, World"); } }
주의할 점은 webEnvironment=RANDOM_PORT를 사용하여 서버를 랜덤 포트로 시작해야 한다. 또한 @LocalServerPort로 포트를 주입받는 것을 주목해야 한다. 또한 @Autowired만 추가하면 Spring Boot가 자동으로 TestRestTemplate를 제공한다.
다른 유용한 접근 방법 중 하나는 서버를 시작하지 않고, Spring에 들어오는 HTTP 요청을 처리하고 컨트롤러에 전달하는 단계 아래의 레이어만 테스트하는 것이다. 이렇게 하면 거의 모든 스택을 사용하면서, 실제 HTTP 요청을 처리하는 것과 동일한 방식으로 코드가 호출되지만 서버를 시작하는 비용은 없다. 이를 위해 @AutoConfigureMockMvc 어노테이션을 사용하여 MockMVC를 주입하여 Spring에 MockMvc를 사용한다.
package com.example.testingWeb; import static org.hamcrest.Matchers.containsString; 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.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; @SpringBootTest @AutoConfigureMockMvc public class TestingWebApplicationTest { @Autowired private MockMvc mockMvc; @Test public void shouldReturnDefaultMessage() throws Exception { this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk()) .andExpect(content().string(containsString("Hello, World"))); } }
이 테스트에서는 전체 Spring 애플리케이션 컨텍스트를 서버 없이 시작한다. @WebMvcTest를 사용하여 웹 레이어만 테스트할 수도 있다.
package com.example.testingWeb; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.test.web.servlet.MockMvc; import static org.hamcrest.Matchers.containsString; 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.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest public class WebLayerTest { @Autowired private MockMvc mockMvc; @Test public void shouldReturnDefaultMessage() throws Exception { this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk()) .andExpect(content().string(containsString("Hello, World"))); } }
테스트의 단언(assertion)은 이전 케이스와 동일하지만 이 테스트에서는 Spring Boot가 전체 컨텍스트 대신 웹 레이어만 인스턴스화한다. 여러 컨트롤러가 있는 애플리케이션에서는 @WebMvcTest(HomeController.class)와 같이 특정 컨트롤러만 인스턴스화하도록 요청할 수도 있다.
package com.example.testingWeb; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class GreetingController { private final GreetingService service; public GreetingController(GreetingService service) { this.service = service; } @RequestMapping("/greeting") public @ResponseBody String greeting() { return service.greet(); } } package com.example.testingWeb; import org.springframework.stereotype.Service; @Service public class GreetingService { public String greet() { return "Hello, World"; } } package com.example.testingWeb; import static org.hamcrest.Matchers.containsString; 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.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.web.servlet.MockMvc; @WebMvcTest(GreetingController.class) public class WebMockTest { @Autowired private MockMvc mockMvc; @MockBean private GreetingService service; @Test public void greetingShouldReturnMessageFromService() throws Exception { when(service.greet()).thenReturn("Hello, Mock"); this.mockMvc.perform(get("/greeting")).andDo(print()).andExpect(status().isOk()) .andExpect(content().string(containsString("Hello, Mock"))); } }
GreetingService에 대한 모의 객체(mock)를 생성하고 주입하기 위해 @MockBean 어노테이션을 사용한다. 그리고 Mockito를 사용하여 해당 모의 객체의 동작을 설정한다.
위에서는 기본적인 스프링 부트에서 테스트가 어떻게 이루어지는지 살펴 보았다.
- mockMvc
- jUnit
- assertThat
이와 같이 3가지를 더 추가적으로 알아볼 계획이다.
728x90반응형'공부 > 자바' 카테고리의 다른 글
17 preview version 안되는 오류 (0) 2023.07.29 JDK 내부의 강력한 캡슐화 (0) 2023.07.27 스프링 부트 3.0 이후 gradle 에러 (0) 2023.07.27 JDK17 (0) 2023.07.27 JAVA Sealed class / interface (0) 2023.07.27