ããã¯ããªã«ãããããŠæžãããã®ïŒ
Spring Web MVCã®ãã¹ãæ¹æ³ããããã¥ã¡ã³ãããã£ããçºããŠèŠãããšãªããªããšã
æã
æ±ã£ãŠããæãããªãé°å²æ°ã§äœ¿ã£ãŠããã®ã§ãã¡ãã£ãšèŠãŠã¿ãããšã«ããŸããã
ããšããªãã±ã§ã¢ãã¯ïŒMockitoïŒã§ãµã€ãã«Beanã®ãã¹ãããããã®ãã€ããŠãããŸãã
ãã®ãšã³ããªãŒã¯ããã¡ãã®ç¶ãçãªæãã§ããã
Spring Boot/Spring Frameworkのテストに関するドキュメントをざっくり眺めてみる - CLOVER🍀
åç §ããããã¥ã¡ã³ããšSpring Web MVCã®ãã¹ã
ä»åãSpring Web MVCã®ãã¹ããèŠãŠããã«ããããåç §ããããã¥ã¡ã³ãã¯äž»ã«æ¬¡ã®2ã€ã§ãã
Spring Bootã®ãã¹ãã«é¢ããããã¥ã¡ã³ãã
ããã«çµã蟌ããšãSpring Boot Applicationã®ãã¹ãã«é¢ããã»ã¯ã·ã§ã³ã§ããã
Core Features / Testing / Testing Spring Boot Applications
Spring Frameworkã®ãã¹ãã«é¢ããããã¥ã¡ã³ãã
ãã¡ããçµã蟌ããšãã€ã³ãã°ã¬ãŒã·ã§ã³ãã¹ãã«é¢ããã»ã¯ã·ã§ã³ã«ãªããŸãã
åºæ¬çã«ã¯ãSpring Bootã®ããã¥ã¡ã³ããèŠãŠããããã¹ãèªäœãæžãæã«äœ¿ãã¯ã©ã¹ã«é¢ããŠã¯Spring Frameworkã®ããã¥ã¡ã³ãã
å¿
èŠã«å¿ããŠèŠãŠããããšããæãã«ãªããŸãã
Spring Web MVCã®ãã¹ããããæ¹æ³ã§ãããSpring BootãSpring Frameworkã®ãã¹ãã«é¢ããããã¥ã¡ã³ããèŠãŠãããš
è€æ°ããããšã«æ°ã¥ããŸãã
èªã¿é²ãæ¹ã§ããããŸãã¯Spring Bootãããžã§ã¯ããäœæããŠãµã³ãã«ããã°ã©ã ãäœãããããããã¹ãã®ããªãšãŒã·ã§ã³ãå€ãã€ã€
ãã¹ãã³ãŒããæžããŠããããšã«ããŸãã
é çªã¯ã以äžã«ããŸãã
@WebMvcTest
ã䜿ã£ããã¹ã- ã¢ãã¯ç°å¢ã§ã®ãã¹ã
- çµã¿èŸŒã¿ãµãŒããŒãèµ·åããŠã®ãã¹ã
äžã«è¡ãã«åŸã£ãŠæéããããããã«ãªããŸãããããæ¬ç©ã®ç°å¢ã«è¿ã¥ããŠãããŸãããšããããæåŸã®ãã¿ãŒã³ã¯ãã¹ãäžã«
çµã¿èŸŒã¿Tomcatãèµ·åããããšã«ãªããŸãã
ãã®ãããããé©å®ããã¥ã¡ã³ãã®å 容ã絡ããªããæžããŠãããããšæããŸããHtmlUnitãSeleniumã䜿ã£ããã¹ãã¯æ±ããŸããã
ã¡ãªã¿ã«ãSpring Frameworkã®ãã¹ãã®ã»ã¯ã·ã§ã³ã«ã¯ãHtmlUnitãWebDriverãGebãšMockMvcãåãããŠäœ¿ãæ¹æ³ã«ã€ããŠæžãããã
ããŠããŸããèå³ãããã°ããã¡ããã©ããã
Testing / Integration / MockMvc / Testing HtmlUnit Integration
ã§ã¯ãé²ããŠãããŸãã
ç°å¢
ä»åã®ç°å¢ã¯ããã¡ãã
$ java --version openjdk 17.0.2 2022-01-18 OpenJDK Runtime Environment (build 17.0.2+8-Ubuntu-120.04) OpenJDK 64-Bit Server VM (build 17.0.2+8-Ubuntu-120.04, mixed mode, sharing) $ mvn --version Apache Maven 3.8.5 (3599d3414f046de2324203b78ddcf9b5e4388aa0) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 17.0.2, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.4.0-107-generic", arch: "amd64", family: "unix"
æºå
ãŸãã¯Spring Bootãããžã§ã¯ããäœæããŸãã
$ curl -s https://start.spring.io/starter.tgz \ -d bootVersion=2.6.6 \ -d javaVersion=17 \ -d name=webmvc-testing \ -d groupId=org.littlewings \ -d artifactId=webmvc-testing \ -d version=0.0.1-SNAPSHOT \ -d packageName=org.littlewings \ -d dependencies=web,webflux \ -d baseDir=webmvc-testing | tar zxvf -
äŸåé¢ä¿ã«web
以å€ã«webflux
ãå
¥ã£ãŠããŸããããã®çç±ã¯åŸã§æžããŸãã
ãããžã§ã¯ãå ãžç§»åã
$ cd webmvc-testing
ãã£ã¬ã¯ããªæ§æã
$ tree . âââ HELP.md âââ mvnw âââ mvnw.cmd âââ pom.xml âââ src âââ main â  âââ java â  â  âââ org â  â  âââ littlewings â  â  âââ WebmvcTestingApplication.java â  âââ resources â  âââ application.properties â  âââ static â  âââ templates âââ test âââ java âââ org âââ littlewings âââ WebmvcTestingApplicationTests.java 12 directories, 7 files
Mavenã§ã®äŸåé¢ä¿ããã³ãã©ã°ã€ã³èšå®ã
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
å«ãŸããŠãããœãŒã¹ã³ãŒãã¯ããããªæãã§ããã
src/main/java/org/littlewings/WebmvcTestingApplication.java
package org.littlewings; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class WebmvcTestingApplication { public static void main(String[] args) { SpringApplication.run(WebmvcTestingApplication.class, args); } }
src/test/java/org/littlewings/WebmvcTestingApplicationTests.java
package org.littlewings; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class WebmvcTestingApplicationTests { @Test void contextLoads() { } }
ãã¹ãã³ãŒãã«ã¯ã@SpringBootTest
ã¢ãããŒã·ã§ã³ãä»äžãããŠããŸãã
ãã®ãããã¯ããã£ããåé€ããŸãã
$ rm src/main/java/org/littlewings/WebmvcTestingApplication.java src/test/java/org/littlewings/WebmvcTestingApplicationTests.java
@SpringBootTestã¢ãããŒã·ã§ã³ã«ã€ããŠ
@SpringBootTest
ã¢ãããŒã·ã§ã³ã«ã€ããŠã®èª¬æãå°ãèŠãŠã¿ãŸãã
@SpringBootTest
ã¢ãããŒã·ã§ã³ã¯ãSpring Bootã®æ©èœãå¿
èŠãªå Žåã«@ContextConfiguration
ã¢ãããŒã·ã§ã³ã®ä»£ããã«äœ¿çšããŸãã
Spring Boot provides a @SpringBootTest annotation, which can be used as an alternative to the standard spring-test @ContextConfiguration annotation when you need Spring Boot features. The annotation works by creating the ApplicationContext used in your tests through SpringApplication. In addition to @SpringBootTest a number of other annotations are also provided for testing more specific slices of an application.
Core Features / Testing / Testing Spring Boot Applications
SpringApplication
ã䜿ããApplicationContext
ã®äœæãè¡ããŸãããŸãããã¹ãã§ã®æ©èœãäžè¶³ããå ŽåãAuto Configurationãè¡ãããã®
æ©èœãæäŸããŠããŸãïŒã¹ã©ã€ã¹ãšèªãã§ããŸãïŒã
ãããŠãWebç°å¢ã®æ¹ã«ç®ãåããŠã¿ããšã@SpringBootTest
ã¢ãããŒã·ã§ã³ã¯ãµãŒããŒãéå§ããªããšæžããŠããŸãã
ã§ãããwebEnvironment
å±æ§ã䜿ã£ãŠãã¹ãã®å®è¡æ¹æ³ã決ããããããã§ãã
By default, @SpringBootTest will not start a server. You can use the webEnvironment attribute of @SpringBootTest to further refine how your tests run:
Core Features / Testing / Testing Spring Boot Applications
@SpringBootTest
ã¢ãããŒã·ã§ã³ã®webEnvironment
å±æ§ã«ã¯ã以äžã®4çš®é¡ãæå®ã§ããŸãã
MOCK
- ããã©ã«ãã®æåã§ãWebçšã®
ApplicationContext
ãããŒãããŠãã¢ãã¯Webç°å¢ãæäŸãã - çµã¿èŸŒã¿ãµãŒããŒã¯èµ·åããªã
@AutoConfigureMockMvc
ã¢ãããŒã·ã§ã³ãŸãã¯@AutoConfigureWebTestClient
ã¢ãããŒã·ã§ã³ãšçµã¿åãããããšã§ãWebã¢ããªã±ãŒã·ã§ã³ã®ã¢ãã¯ããŒã¹ã®ãã¹ããå¯èœã«ãªã
- ããã©ã«ãã®æåã§ãWebçšã®
RANDOM_PORT
WebServerApplicationContext
ãããŒãããŠãæ¬ç©ã®Webç°å¢ãæäŸãã- çµã¿èŸŒã¿ãµãŒããŒãèµ·åããã©ã³ãã ãªããŒãã§ãªãã¹ã³ãã
DEFINED_PORT
WebServerApplicationContext
ãããŒãããŠãæ¬ç©ã®Webç°å¢ãæäŸãã- çµã¿èŸŒã¿ãµãŒããŒãèµ·åãã
application.properties
ã§å®çŸ©ãããããŒããŸãã¯ããã©ã«ãã®8080
ããŒãã§ãªãã¹ã³ãã
NONE
SpringApplication
ã«ãã£ãŠApplicationContext
ã¯ããŒããããã®ã®ãã¢ãã¯ããã以å€ã®ãããã®Webç°å¢ãæäŸããªã
ãã¹ããã¢ãã¯Webç°å¢ã§è¡ãããçµã¿èŸŒã¿ãµãŒããŒã§è¡ããããšããæãã«ãªããŸãïŒãŸã£ããWebç°å¢ã䜿ããªããšããéžæè¢ããããŸããïŒã
Spring Web MVCã䜿çšå¯èœãªå Žåã¯ãSpring Web MVCããŒã¹ã®ApplicationContext
ãæ§æãããããã§ãã
If Spring MVC is available, a regular MVC-based application context is configured.
Core Features / Testing / Testing Spring Boot Applications / Detecting Web Application Type
ãµã³ãã«ã¢ããªã±ãŒã·ã§ã³ãäœæãã
ãã¹ã察象ã®ãµã³ãã«ã¢ããªã±ãŒã·ã§ã³ãäœæããŸãããããé¡ã¯ãã¡ãã£ãšããechoããã°ã©ã ã«ããŸãã
mainã¯ã©ã¹ã
src/main/java/org/littlewings/spring/webmvc/App.java
package org.littlewings.spring.webmvc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class App { public static void main(String... args) { SpringApplication.run(App.class, args); } }
RestControllerãGETãšPOSTçšã®ã¡ãœãããçšæããŠãPOSTã®å Žåã¯JSONã§ãªã¯ãšã¹ããåãåãããã«ããŸãã
src/main/java/org/littlewings/spring/webmvc/EchoController.java
package org.littlewings.spring.webmvc; import java.util.Optional; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("echo") public class EchoController { MessageDecorationService messageDecorationService; public EchoController(MessageDecorationService messageDecorationService) { this.messageDecorationService = messageDecorationService; } @GetMapping("get") public EchoResponse get(@RequestParam String message) { Thread.dumpStack(); return EchoResponse .create( messageDecorationService .decorate(Optional.ofNullable(message).orElse("Hello World[get]!!")), Thread.currentThread().getName() ); } @PostMapping("post") public EchoResponse post(@RequestBody EchoRequest echoRequest) { Thread.dumpStack(); return EchoResponse .create( messageDecorationService .decorate(Optional.ofNullable(echoRequest.getMessage()).orElse("Hello World[post]!!")), Thread.currentThread().getName() ); } }
ã¬ã¹ãã³ã¹ã«ã¯åãåã£ãã¡ãã»ãŒãžãServiceã§å å·¥ããŠè¿ãããã®æã«åäœããŠããã¹ã¬ããåãè¿ãããã«ããŸãã
ãããããRestControllerã®ã¡ãœããåŒã³åºãæã«ã¯ã¹ã¿ãã¯ãã¬ãŒã¹ãåºåããããã«ããŠããŸãã
ãªã¯ãšã¹ãçšã®ã¯ã©ã¹ã
src/main/java/org/littlewings/spring/webmvc/EchoRequest.java
package org.littlewings.spring.webmvc; public class EchoRequest { String message; public static EchoRequest create(String message) { EchoRequest echoRequest = new EchoRequest(); echoRequest.setMessage(message); return echoRequest; } // getterïŒsetterã¯çç¥ }
ã¬ã¹ãã³ã¹çšã®ã¯ã©ã¹ã
src/main/java/org/littlewings/spring/webmvc/EchoResponse.java
package org.littlewings.spring.webmvc; public class EchoResponse { String message; String threadName; public static EchoResponse create(String message, String threadName) { EchoResponse echoResponse = new EchoResponse(); echoResponse.setMessage(message); echoResponse.setThreadName(threadName); return echoResponse; } // getterïŒsetterã¯çç¥ }
RestControllerã䜿ãServiceã¯ã©ã¹ã
src/main/java/org/littlewings/spring/webmvc/MessageDecorationService.java
package org.littlewings.spring.webmvc; import org.springframework.stereotype.Service; @Service public class MessageDecorationService { public String decorate(String message) { return String.format("â â â %s !!â â â ", message); } }
åäœç¢ºèªã
$ mvn spring-boot:run
OKã§ããã
$ curl localhost:8080/echo/get?message=Hello {"message":"â â â Hello !!â â â ","threadName":"http-nio-8080-exec-1"} $ curl -H 'Content-Type: application/json' localhost:8080/echo/post -d '{"message": "Hello"}' {"message":"â â â Hello !!â â â ","threadName":"http-nio-8080-exec-2"}
ã§ã¯ããã¡ãã«å¯ŸããŠãã¹ããæžããŠãããŸãããããªããäŸåé¢ä¿ã«spring-boot-starter-webflux
ãå«ãŸããŠããŸããããç¹ã«æ瀺çã«
説æããªãéãã¯spring-boot-starter-web
ãšspring-boot-starter-test
ãäŸåé¢ä¿ã«å«ãŸããŠããã°ãã¹ãã¯åäœããŸãã
spring-boot-starter-webflux
ã«ã€ããŠã¯ãå¿
èŠãªéšåã§èª¬æããŸãã
ããã©ã«ãã®@SpringBootTestã§çµã¿èŸŒã¿ãµãŒããŒãèµ·åããªãããšã確èªãã
å®éã®ãã¹ãã«è¡ãåã«ã@SpringBootTest
ã¢ãããŒã·ã§ã³ã ãã§ã¯çµã¿èŸŒã¿ãµãŒããŒãèµ·åããªãããšã確èªããŠãããŸãã
ãããªãã¹ãã§ç¢ºèªã
src/test/java/org/littlewings/spring/webmvc/SpringBootTestDefaultTest.java
package org.littlewings.spring.webmvc; import java.io.IOException; import java.net.ConnectException; import java.net.HttpURLConnection; import java.net.URI; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import static org.assertj.core.api.Assertions.assertThatThrownBy; @SpringBootTest public class SpringBootTestDefaultTest { @Test public void test() throws IOException { HttpURLConnection conn = (HttpURLConnection) URI.create("http://localhost:8080").toURL().openConnection(); assertThatThrownBy(() -> conn.connect()) .isInstanceOf(ConnectException.class) .hasMessage("æ¥ç¶ãæåŠãããŸãã"); } }
ããã¥ã¡ã³ãã©ãããµãŒããŒãèµ·åããŠããªãã®ã§ãæ¥ç¶ãæåŠãããŸãã
ã§ã¯ãå ã«é²ããŠãããŸãããã
@WebMvcTestã䜿ã£ãŠãã¹ããæžã
ããã¥ã¡ã³ãã®é çªãšã¯ã ãã¶ç°ãªããŸãããæåã«@WebMvcTest
ã¢ãããŒã·ã§ã³ã䜿ã£ããã¹ããæžãããšã«ããŸãã
Core Features / Testing / Testing Spring Boot Applications / Auto-configured Spring MVC Tests
ãã¡ãã¯ãSpring Web MVCã䜿ã£ãŠå®è£ ãããç¹å®ã®Controllerãåäœãããããã¹ãããŸãã
@WebMvcTest
ã¢ãããŒã·ã§ã³ã䜿çšãããšãSpring Web MVCãæ§æãããšãšãã«éãããBeanãã¹ãã£ã³ããŸãã
@Controller
@ControllerAdvice
@JsonComponent
Converter
GenericConverter
Filter
HandlerInterceptor
WebMvcConfigurer
WebMvcRegistrations
HandlerMethodArgumentResolver
éåžžã®@Component
ã@ConfigurationProperties
ãšãã£ãBeanã¯ã@WebMvcTest
ã¢ãããŒã·ã§ã³ã䜿çšããŠãã¹ãã£ã³ãããŸããã
䜿çšãããå Žåã¯ã@EnableConfigurationProperties
ã¢ãããŒã·ã§ã³ãä»äžããããšã«ãªããŸãã
ãªãã@WebMvcTest
ã¢ãããŒã·ã§ã³ã䜿çšããæã«æå¹ã«ãªãAuto Configurationã¯ã以äžã«ãŸãšããããŠããŸãã
Test Auto-configuration Annotations
@WebMvcTest
ã¢ãããŒã·ã§ã³ã䜿çšããŠäœæãããã¹ãã¯ããã¡ãã
src/test/java/org/littlewings/spring/webmvc/WebMvcTestTest.java
package org.littlewings.spring.webmvc; import com.fasterxml.jackson.databind.ObjectMapper; import org.hamcrest.Matchers; 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.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import static org.mockito.BDDMockito.given; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @WebMvcTest(EchoController.class) public class WebMvcTestTest { @Autowired MockMvc mvc; @MockBean MessageDecorationService messageDecorationService; @Autowired ObjectMapper objectMapper; @Test public void getTest() throws Exception { given(messageDecorationService.decorate("WebMvcTest[get]")) .willReturn("*** WebMvcTest[get] !!***"); // queryParam mvc .perform( get("/echo/get") .queryParam("message", "WebMvcTest[get]") ) .andExpect(status().isOk()) .andExpect( content() .json( objectMapper.writeValueAsString( EchoResponse.create("*** WebMvcTest[get] !!***", "main") ) ) ); // querystring mvc .perform( get("/echo/get?message=WebMvcTest[get]") ) .andExpect(status().isOk()) .andExpect(jsonPath("message", Matchers.is("*** WebMvcTest[get] !!***"))) .andExpect(jsonPath("threadName", Matchers.is("main"))); } @Test public void postTest() throws Exception { given(messageDecorationService.decorate("WebMvcTest[post]")) .willReturn("*** WebMvcTest[post] !!***"); mvc .perform( post("/echo/post") .contentType(MediaType.APPLICATION_JSON) .content( objectMapper.writeValueAsString( EchoRequest.create("WebMvcTest[post]") ) ) ) .andExpect(status().isOk()) .andExpect(header().string("content-type", MediaType.APPLICATION_JSON_VALUE)) .andExpect(jsonPath("message", Matchers.is("*** WebMvcTest[post] !!***"))) .andExpect(jsonPath("threadName", Matchers.is("main"))); } }
ãã¹ãã¯ã©ã¹ã«@WebMvcTest
ã¢ãããŒã·ã§ã³ãä»äžããŸãã
@WebMvcTest(EchoController.class) public class WebMvcTestTest {
ããã¥ã¡ã³ãã«ç¿ã£ãŠControllerã@WebMvcTest
ã¢ãããŒã·ã§ã³ã«æå®ããŠããŸãããå®ã¯æå®ããªããŠãåããŸãã
ã¹ãã£ã³å¯Ÿè±¡ã«@Controller
ãå
¥ã£ãŠããããã§ããã
ControllerãäŸåããBeanã¯ãã¢ãã¯ã«ããŸãã
@MockBean
MessageDecorationService messageDecorationService;
ãšããããæåã«æžããããã«ã¹ãã£ã³ãããBeanãéå®ãããŠããã®ã§ãä»åã®ããã«å¥ã®Beanã«äŸåããŠããControllerã¯ã¢ãã¯ã§
äŸåé¢ä¿ããªããšããããªãBeanãç»é²ãããªãããªããšåäœããŸããã
ããä»åã®ãœãŒã¹ã³ãŒãããã¢ãã¯ã«é¢ããã³ãŒããåé€ãããšãäœæããRestControllerã®äŸåé¢ä¿ã解決ã§ããã«ãã¹ãã«å€±æããŸãã
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'echoController' defined in file [/path/to/webmvc-testing/target/classes/org/littlewings/spring/webmvc/EchoController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.littlewings.spring.webmvc.MessageDecorationService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {} at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800) at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1372) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1222) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:953) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:740) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:415) at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:136) at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99) at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124) ... 72 more Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.littlewings.spring.webmvc.MessageDecorationService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {} at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1799) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1355) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1309) at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887) at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791) ... 90 more
ãã¹ãã«ã¯MockMvc
ã䜿çšããŸãã
@Autowired
MockMvc mvc;
MockMvc
èªäœã«ã€ããŠã¯ããã¡ããåç
§ã
Testing / Integration Testing / MockMvc
ä»åã¯ãæåã«äŸåããServiceã®ã¢ãã¯ãèšå®ããŠãããããMockMvc
ã䜿ã£ãŠãã¹ãããŠããŸãã
@Test public void getTest() throws Exception { given(messageDecorationService.decorate("WebMvcTest[get]")) .willReturn("*** WebMvcTest[get] !!***"); // queryParam mvc .perform( get("/echo/get") .queryParam("message", "WebMvcTest[get]") ) .andExpect(status().isOk()) .andExpect( content() .json( objectMapper.writeValueAsString( EchoResponse.create("*** WebMvcTest[get] !!***", "main") ) ) ); // querystring mvc .perform( get("/echo/get?message=WebMvcTest[get]") ) .andExpect(status().isOk()) .andExpect(jsonPath("message", Matchers.is("*** WebMvcTest[get] !!***"))) .andExpect(jsonPath("threadName", Matchers.is("main"))); }
ä»åã¯ããªããšãªãQueryStringãqueryParam
ã§èšå®ããããçŽæ¥URLã«ä»ãããããŠã¿ãŸããããŸããJSONã¬ã¹ãã³ã¹ã®ã¢ãµãŒã·ã§ã³æ¹æ³ã
åããŠããŸãã
ã¢ãµãŒã·ã§ã³çµæãèŠããšãããã®ã§ãããä»åã®ãã¹ãã§ã¯RestControllerã¯mainã¹ã¬ããã§åäœããŠããŸããã
.andExpect(jsonPath("message", Matchers.is("*** WebMvcTest[get] !!***"))) .andExpect(jsonPath("threadName", Matchers.is("main")));
RestControllerã®ã¡ãœããåŒã³åºãæã®ã¹ã¿ãã¯ãã¬ãŒã¹ã¯ãã¡ãã
java.lang.Exception: Stack trace at java.base/java.lang.Thread.dumpStack(Thread.java:1380) at org.littlewings.spring.webmvc.EchoController.get(EchoController.java:23) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) at javax.servlet.http.HttpServlet.service(HttpServlet.java:655) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:72) at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167) at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:199) at org.littlewings.spring.webmvc.WebMvcTestTest.getTest(WebMvcTestTest.java:35) ãçç¥ã
ãã®å°ãåŸã«æžãã¢ãã¯ç°å¢ã§ã®ãã¹ãã«ã€ããŠæžããå
容ãèŠããšããããŸããã@WebMvcTest
ã¢ãããŒã·ã§ã³ã䜿ã£ãæãã¢ãã¯ç°å¢ã§
åäœããŠããããšã«ãªããŸãã
MockMvcã«ã€ããŠ
MockMvc
ã«ã€ããŠãããå°ãæ·±å ããŠã¿ãŸãããã
MockMvc
ã«ã¯ã¹ã¿ã³ãã¢ãã³ã¢ãŒããšSpringã®Configurationã䜿ã£ãã¢ãŒãã®2ã€ã®ã»ããã¢ããæ¹æ³ããããŸãã
Testing / Integration Testing / MockMvc / Setup Choices
Spring Bootã§ã¯ãSpringã®Configurationã䜿ã£ãã¢ãŒãã§ã»ããã¢ããããããã§ãã
MockMvc
ã®ã»ããã¢ããèªäœã¯å®äºããŠããã®ã§ãããšã¯ãªã¯ãšã¹ããèšå®ããããã¬ã¹ãã³ã¹ãã¢ãµãŒã·ã§ã³ããããšãã£ãæãã®
䜿ãæ¹ã«ãªããŸãã
Testing / Integration Testing / MockMvc / Performing Requests
Testing / Integration Testing / MockMvc / Defining Expectations
ä»åã¯éåžžã®Controllerã§äœ¿ããããªããããªãã©ã¯ãŒãå
ãã¢ãã«ã®ã¢ãµãŒã·ã§ã³ã¯è¡ããŸãããããµã³ãã«ãããã®ã§ãã¡ãã
åèã«ãããšããã§ãããã
Servlet Filterãç»é²ããæ©èœãããããã§ãã
Testing / Integration Testing / MockMvc / Filter Registrations
ãã¡ãã¯ã@WebMvcTest
ã¢ãããŒã·ã§ã³ã®å±æ§ã§è¡šçŸããããšã«ãªãããã§ãã
WebMvcTest (Spring Boot 2.6.6 API)
ããã¥ã¡ã³ããèŠããšã@AutoConfigureMockMvc
ã¢ãããŒã·ã§ã³ã䜿ããšããããšã¯æžãããŠããŸããã
Core Features / Testing / Testing Spring Boot Applications / Auto-configured Spring MVC Tests
ãŸããMockMvc
ãšE2Eãã¹ãã®æ¯èŒã«ã€ããŠãããã¥ã¡ã³ãã«æžãããŠããŸãã
Testing / Integration Testing / MockMvc / MockMvc vs End-to-End Tests
MockMvc
ã¯Servletã®ã¢ãã¯å®è£
ã«åºã¥ããŠæ§ç¯ãããŠããŠãå®éã®ãµãŒãã¬ããã³ã³ãããšæ¯ã¹ããšç°ãªãéšåããããŸãã
jsessoinid
Cookieããªãããã©ã¯ãŒãããªãã€ã¬ã¯ãããµãŒãã¬ããã®äŸå€ãã³ããªã³ã°ã¯è¡ããããïŒSpring Bootã§ã¯ããŸãé¢ä¿ãªãã§ããïŒ
JSPã®ã¬ã³ããªã³ã°ãè¡ãããŸããã
â»Theymeleafãªã©ã®ãã³ãã¬ãŒããšã³ãžã³ã§ã®ã¬ã³ããªã³ã°ããJSONãXMLãªã©ã§ã®ã¬ã¹ãã³ã¹ã¯è¡ããããã§ã
MockMvc
ã䜿çšãããã¹ãã§ã¯Controllerã«äŸåããBeanãã¢ãã¯åãããããŠãWebã¬ã€ã€ãŒã«éå®ãããã¹ãããããããªããšããã
ãã€ã³ãã§ããSpringãšããŠã¯MockMvc
ã®å©çšãã€ã³ãã°ã¬ãŒã·ã§ã³ãã¹ãã«äœçœ®ã¥ããŠããŸãããåäœãã¹ãã«è¿ããã®ã§ã¯ãããŸãã
ãã®ãããE2Eãã¹ãã®ãããªãã©ãã¯ããã¯ã¹çãªãã¹ããšç°ãªããMockMvc
ã§ã¯HandlerïŒControllerïŒã䜿ãããããšãäŸå€åŠçã§
HandlerExceptionResolver
ã䜿ãããããšãã¢ãã«ã®å±æ§ããã€ã³ãã£ã³ã°ãšã©ãŒãªã©ã確èªã§ããŸãã
ãã®ãããªç¹æ§ãèžãŸãã€ã€ãã©ã®ãã¹ãã䜿ããã決ããŠããããšã«ãªããã§ããããã
ã¢ãã¯ç°å¢ã§ãã¹ããã
次ã¯ãã¢ãã¯ç°å¢ã§ã®ãã¹ããè¡ããŸãã
Core Features / Testing / Testing Spring Boot Applications / Testing with a mock environment
å
·äœçã«ã¯ã@SpringBootTest
ã¢ãããŒã·ã§ã³ãš@AutoConfigureMockMvc
ã¢ãããŒã·ã§ã³ã®çµã¿åããã«ãªããŸãã
äœæãããã¹ãã³ãŒãã¯ãã¡ãã
src/test/java/org/littlewings/spring/webmvc/AutoConfigureMockMvcTest.java
package org.littlewings.spring.webmvc; import com.fasterxml.jackson.databind.ObjectMapper; import org.hamcrest.Matchers; 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.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @SpringBootTest @AutoConfigureMockMvc public class AutoConfigureMockMvcTest { @Test public void getTest(@Autowired MockMvc mvc) throws Exception { mvc .perform( get("/echo/get") .queryParam("message", "MockMvc[get]") ) .andExpect(status().isOk()) .andExpect(jsonPath("message", Matchers.is("â â â MockMvc[get] !!â â â "))) .andExpect(jsonPath("threadName", Matchers.is("main"))); } @Test public void postTest(@Autowired MockMvc mvc, @Autowired ObjectMapper objectMapper) throws Exception { mvc .perform( post("/echo/post") .contentType(MediaType.APPLICATION_JSON) .content( objectMapper.writeValueAsString( EchoRequest.create("MockMvc[post]") ) ) ) .andExpect(status().isOk()) .andExpect(header().string("content-type", MediaType.APPLICATION_JSON_VALUE)) .andExpect(jsonPath("message", Matchers.is("â â â MockMvc[post] !!â â â "))) .andExpect(jsonPath("threadName", Matchers.is("main"))); } }
@WebMvcTest
ã¢ãããŒã·ã§ã³ã䜿ã£ãæãšåãããã«MockMvc
ã䜿ã£ããã¹ãã«ãªããŸãããå
ã»ã©ãšã®éãã¯RestControllerã
äŸåããBeanãã¢ãã¯åããŠããªãããšã§ããã
ãŸããã¹ãã¯ã©ã¹ã«ä»äžããã¢ãããŒã·ã§ã³ã§ãæ瀺çãªControllerã®æå®ããªããªããŸããã
@SpringBootTest @AutoConfigureMockMvc public class AutoConfigureMockMvcTest {
RestControllerã®ã¡ãœããåŒã³åºãæã®ã¹ã¿ãã¯ãã¬ãŒã¹ã¯ããã¡ãã
java.lang.Exception: Stack trace at java.base/java.lang.Thread.dumpStack(Thread.java:1380) at org.littlewings.spring.webmvc.EchoController.get(EchoController.java:23) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) at javax.servlet.http.HttpServlet.service(HttpServlet.java:655) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:72) at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167) at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:199) at org.littlewings.spring.webmvc.AutoConfigureMockMvcTest.getTest(AutoConfigureMockMvcTest.java:22) ãçç¥ã
ããèŠããšã@WebMvcTest
ã¢ãããŒã·ã§ã³ã䜿ã£ãæã®ã¹ã¿ãã¯ãã¬ãŒã¹ãšãŸã£ããåãã§ãã
ãããŠãã¹ã¬ããåãmain
ã§ããããšã確èªã§ããŸãã
.andExpect(jsonPath("message", Matchers.is("â â â MockMvc[post] !!â â â "))) .andExpect(jsonPath("threadName", Matchers.is("main")));
ãšããããšã¯ããã®2ã€ã®ãã¹ãæ¹æ³ã§ã¯ãåãã¢ãã¯ç°å¢ã§åäœããŠããããšã«ãªããŸãããéãã¯Beanã®ã¹ãã£ã³ç¯å²ãšãã£ããšãã
ã§ããããïŒ@SpringBootTest
ã¢ãããŒã·ã§ã³ã䜿ã£ãŠããå·®ããšïŒã
ã¡ãªã¿ã«ããã®ãã¹ãã®äžã«@MockBean
ã¢ãããŒã·ã§ã³ã䜿ã£ãã³ãŒããè¿œå ããããšã§ãäŸåããBeanãã¢ãã¯åããããšã¯
ãµã€ãã«ã§ããŸãã
@MockBean
MessageDecorationService messageDecorationService;
çµã¿èŸŒã¿ãµãŒããŒãèµ·åããŠãã¹ããã
æåŸã¯ãçµã¿èŸŒã¿ãµãŒããŒãèµ·åããŠãã¹ããè¡ããŸãã
Core Features / Testing / Testing Spring Boot Applications / Testing with a running server
ãã¡ãã¯ã@SpringBootTest
ã¢ãããŒã·ã§ã³ã®webEnvironment
å±æ§ã«RANDOM_PORT
ãŸãã¯DEFINED_PORT
ãæå®ããããšã§
å®çŸã§ããŸããä»åã¯ãRANDOM_PORT
ã䜿çšããŸãã
äœæãããã¹ãã¯ãã¡ãã
src/test/java/org/littlewings/spring/webmvc/SpringBootTestWebEnvironmentTest.java
package org.littlewings.spring.webmvc; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.web.reactive.server.WebTestClient; import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "server.tomcat.threads.max=1" ) public class SpringBootTestWebEnvironmentTest { @Test public void getTest(@Autowired WebTestClient webClient) { webClient .get() .uri(uriBuilder -> uriBuilder .path("/echo/get") .queryParam("message", "WebEnvironment Test[get]") .build() ) .exchange() .expectStatus().isOk() .expectBody(EchoResponse.class) .consumeWith(result -> { EchoResponse response = result.getResponseBody(); assertThat(response.getMessage()).isEqualTo("â â â WebEnvironment Test[get] !!â â â "); assertThat(response.getThreadName()).isEqualTo("http-nio-auto-1-exec-1"); }); } @Test public void postTest(@Autowired WebTestClient webClient) { webClient .post() .uri("/echo/post") .contentType(MediaType.APPLICATION_JSON) .bodyValue(EchoRequest.create("WebEnvironment Test[post]")) .exchange() .expectStatus().isOk() .expectBody(EchoResponse.class) .consumeWith(result -> { EchoResponse response = result.getResponseBody(); assertThat(response.getMessage()).isEqualTo("â â â WebEnvironment Test[post] !!â â â "); assertThat(response.getThreadName()).isEqualTo("http-nio-auto-1-exec-1"); }); } }
@SpringBootTest
ã¢ãããŒã·ã§ã³ã®webEnvironment
å±æ§ã¯RANDOM_PORT
ãšãããŸãRestControllerãã¹ã¬ããåãè¿ãã®ã§
åºå®ã«ãªãããã«çµã¿èŸŒã¿Tomcatã®ã¹ã¬ããããŒã«ã¯1ã«ããŸããã
@SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "server.tomcat.threads.max=1" ) public class SpringBootTestWebEnvironmentTest {
ãã¹ãã«ã¯ãWebTestClient
ã䜿ãããã§ãã
@Test public void getTest(@Autowired WebTestClient webClient) { webClient .get() .uri(uriBuilder -> uriBuilder .path("/echo/get") .queryParam("message", "WebEnvironment Test[get]") .build() ) .exchange() .expectStatus().isOk() .expectBody(EchoResponse.class) .consumeWith(result -> { EchoResponse response = result.getResponseBody(); assertThat(response.getMessage()).isEqualTo("â â â WebEnvironment Test[get] !!â â â "); assertThat(response.getThreadName()).isEqualTo("http-nio-auto-1-exec-1"); }); }
ãã®ããã«ãäŸåé¢ä¿ã«spring-boot-starter-webflux
ãè¿œå ããŠããŸãã
Core Features / Testing / Testing Spring Boot Applications / Testing with a running server
Testing / Integration Testing / WebTestClient
ã¡ãªã¿ã«ãWebTestClient
ã¯ã¢ãã¯ç°å¢ã§ãå©çšã§ããããã§ãã
Spring WebFluxãäŸåé¢ä¿ã«è¿œå ããããªãå Žåã¯ãTestRestTemplate
ã䜿ã£ãŠãã¹ãããŠãOKã§ãã
RestControlleråŒã³åºãæã®ã¹ã¿ãã¯ãã¬ãŒã¹ã¯ãã¡ãã
java.lang.Exception: Stack trace at java.base/java.lang.Thread.dumpStack(Thread.java:1380) at org.littlewings.spring.webmvc.EchoController.get(EchoController.java:23) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) at javax.servlet.http.HttpServlet.service(HttpServlet.java:655) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:889) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1743) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.base/java.lang.Thread.run(Thread.java:833)
Tomcatã®ã¹ã¬ããããŒã«å ã§åäœããŠããããšãããããŸããã
ãŸããã¢ãµãŒã·ã§ã³æã«ç¢ºèªããŠããã¹ã¬ããåããmain
ã§ã¯ãªããªã£ãŠããŸããã
.consumeWith(result -> { EchoResponse response = result.getResponseBody(); assertThat(response.getMessage()).isEqualTo("â â â WebEnvironment Test[get] !!â â â "); assertThat(response.getThreadName()).isEqualTo("http-nio-auto-1-exec-1"); });
Beanã®ã¢ãã¯åãè¡ããªããSpring Web MVCã¢ããªã±ãŒã·ã§ã³ãšããŠå®å
šãªåœ¢ã§åäœããŸããE2Eãã¹ããšãã£ãçšéã®ã€ã¡ãŒãžã§
䜿ãããšã«ãªããã§ããããã
ããã§ã²ãšãšããSpring Web MVCã®ãã¹ãæ¹æ³ã®ç¢ºèªã¯ã§ããæãã§ããããã
ãªãã±ïŒã¢ãã¯ã䜿ã£ãBeanã®ãã¹ã
ãããŸã§Spring Web MVCã®ãã¹ããèŠãŠããŸãããããµã€ãã®Beanã®ãã¹ãã®ãã¿ãŒã³ãå®å šã«é£ã°ããŠããŸãã
軜ããã£ãŠãããŸããããåäœãã¹ããªæãã§ããã°ã€ã³ã¹ã¿ã³ã¹ãnew
ããŠãè¯ããšæãã®ã§ãããä»åã¯ã¢ãã¯ã䜿ã£ãŠã¿ãŸãã
Core Features / Testing / Testing Spring Boot Applications / Mocking and Spying Beans
ãŸãã¯ãServiceãããã²ãšã€è¿œå ããŸãããã
src/main/java/org/littlewings/spring/webmvc/WordsJoinService.java
package org.littlewings.spring.webmvc; import java.util.Arrays; import java.util.stream.Collectors; import org.springframework.stereotype.Service; @Service public class WordsJoinService { MessageDecorationService messageDecorationService; public WordsJoinService(MessageDecorationService messageDecorationService) { this.messageDecorationService = messageDecorationService; } public String join(String... words) { return messageDecorationService.decorate(Arrays.stream(words).collect(Collectors.joining(" "))); } }
ãã®Serviceã§ã¯ãæåã«äœæããServiceã䜿ã£ãŠããŸãã
ãã¹ãã¯ãã¡ãã
src/test/java/org/littlewings/spring/webmvc/MockingBeansTest.java
package org.littlewings.spring.webmvc; 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.mock.mockito.MockBean; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.mockito.BDDMockito.given; @SpringBootTest public class MockingBeansTest { @Autowired WordsJoinService wordsJoinService; @MockBean MessageDecorationService messageDecorationService; @Test public void serviceTest() { given(messageDecorationService.decorate("Hello World")) .willReturn("*** Hello World !!***"); assertThat(wordsJoinService.join("Hello", "World"), is("*** Hello World !!***")); } }
ãã¹ãã¯ã©ã¹ã¯@SpringBootTest
ã¢ãããŒã·ã§ã³ã䜿ã£ãŠäœæããŠããã¹ã察象ã®Beanã@Autowired
ãã€ã€ãäŸåããBeanã¯
@MockBean
ã¢ãããŒã·ã§ã³ã§ã¢ãã¯åããã°OKã§ãã
ãŸãšã
Spring Web MVCã§äœæããã¢ããªã±ãŒã·ã§ã³ã®ãã¹ãæ¹æ³ãèŠãŠã¿ãŸããã
ããŸãç解ã§ããŠããªãã£ãã®ã§ãæéãåã£ãŠä»åæŽçããŠã¿ãŠè¯ãã£ãããªããšæããŸãã