创建 Spring 应用程序然后使用 JUnit 对其进行测试

本指南将引导您完成创建 Spring 应用程序然后使用 JUnit 对其进行测试的过程。

您将构建什么

您将构建一个简单的 Spring 应用程序并使用 JUnit 对其进行测试。您可能已经知道如何编写和运行应用程序中各个类的单元测试,因此,在本指南中,我们将专注于使用 Spring 测试和 Spring Boot 功能来测试 Spring 和代码之间的交互。您将从应用程序上下文成功加载的简单测试开始,然后继续使用 Spring 的 .??MockMvc??

你需要什么约15分钟最喜欢的文本编辑器或 IDEJDK 1.8或以后格拉德尔 4+?或梅文 3.2+您也可以将代码直接导入到 IDE 中:弹簧工具套件 (STS)智能理念VSCode如何完成本指南

像大多数春天一样入门指南,您可以从头开始并完成每个步骤,也可以绕过您已经熟悉的基本设置步骤。无论哪种方式,您最终都会得到工作代码。

要从头开始,请继续从 Spring 初始化开始.

要跳过基础知识,请执行以下操作:

下载?并解压缩本指南的源存储库,或使用吉特:git clonehttps://github.com/spring-guides/gs-testing-web.git光盘成gs-testing-web/initial跳转到创建简单的应用程序.

完成后,您可以根据 中的代码检查结果。??gs-testing-web/complete??

从 Spring 初始化开始

你可以使用这个预初始化项目,然后单击生成以下载 ZIP 文件。此项目配置为适合本教程中的示例。

手动初始化项目:

导航到https://start.spring.io.此服务拉入应用程序所需的所有依赖项,并为您完成大部分设置。选择 Gradle 或 Maven 以及您要使用的语言。本指南假定您选择了 Java。单击“依赖关系”,然后选择“Spring Web”。单击生成。下载生成的 ZIP 文件,该文件是配置了您选择的 Web 应用程序的存档。

如果您的 IDE 集成了 Spring Initializr,则可以从 IDE 完成此过程。

您也可以从 Github 分叉项目,然后在 IDE 或其他编辑器中打开它。

创建简单的应用程序

为您的 Spring 应用程序创建一个新控制器。以下清单(来自 )显示了如何执行此操作:??src/main/java/com/example/testingweb/HomeController.java??

package com.example.testingweb;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;@Controllerpublic class HomeController { @RequestMapping(“/”) public @ResponseBody String greeting() { return “Hello, World”; }}

前面的示例未指定 vs、 等。默认情况下,映射所有 HTTP 操作。您可以使用 或 缩小此映射的范围。??GET????PUT????POST????@RequestMapping????@GetMapping????@RequestMapping(method=GET)??

运行应用程序

Spring 初始化为您创建一个应用程序类(带有方法的类)。对于本指南,您无需修改此类。下面的清单(来自)显示了 Spring 初始化器创建的应用程序类:??main()????src/main/java/com/example/testingweb/TestingWebApplication.java??

package com.example.testingweb;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class TestingWebApplication { public static void main(String[] args) { SpringApplication.run(TestingWebApplication.class, args); }}

??@SpringBootApplication??是一个方便的注释,它添加了以下所有内容:

??@Configuration??:将类标记为应用程序上下文的 Bean 定义源。??@EnableAutoConfiguration??:告诉 Spring 引导根据类路径设置、其他 bean 和各种属性设置开始添加 bean。??@EnableWebMvc??:将应用程序标记为 Web 应用程序并激活关键行为,例如设置 .Spring Boot 在看到类路径时会自动添加它。DispatcherServletspring-webmvc??@ComponentScan??:告诉 Spring 在包中查找其他组件、配置和服务,让它找到类。com.example.testingwebHelloController

该方法使用 Spring Boot 的方法启动应用程序。您是否注意到没有一行 XML?也没有文件。此 Web 应用程序是 100% 纯 Java,您无需处理配置任何管道或基础结构。Spring Boot为您处理所有这些。??main()????SpringApplication.run()????web.xml??

将显示日志记录输出。该服务应在几秒钟内启动并运行。

测试应用程序

现在应用程序正在运行,您可以对其进行测试。您可以在 上加载主页。但是,为了让自己更确信应用程序在您进行更改时可以正常工作,您需要自动执行测试。??http://localhost:8080??

Spring 引导假定您计划测试您的应用程序,因此它会将必要的依赖项添加到构建文件( 或 )。??build.gradle????pom.xml??

您可以做的第一件事是编写一个简单的健全性检查测试,如果应用程序上下文无法启动,该测试将失败。以下清单(来自 )显示了如何执行此操作:??src/test/java/com/example/testingweb/TestingWebApplicationTest.java??

package com.example.testingweb;import org.junit.jupiter.api.Test;import org.springframework.boot.test.context.SpringBootTest;@SpringBootTestpublic class TestingWebApplicationTests { @Test public void contextLoads() { }}

该注释告诉 Spring 引导查找一个主配置类(例如,一个带有 的配置类),并使用它来启动 Spring 应用程序上下文。您可以在 IDE 中或在命令行上运行此测试(通过运行 或 ),它应该通过。为了说服自己上下文正在创建控制器,您可以添加一个断言,如以下示例 (from ) 所示:??@SpringBootTest????@SpringBootApplication????./mvnw test????./gradlew test????src/test/java/com/example/testingweb/SmokeTest.java??

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;@SpringBootTestpublic class SmokeTest { @Autowired private HomeController controller; @Test public void contextLoads() throws Exception { assertThat(controller).isNotNull(); }}

Spring 解释注释,并在运行测试方法之前注入控制器。我们使用??@Autowired??断言J(其中提供和其他方法)来表达测试断言。??assertThat()??

Spring 测试支持的一个很好的功能是应用程序上下文在测试之间缓存。这样,如果一个测试用例中有多个方法或具有相同配置的多个测试用例,则它们只会产生启动应用程序一次的成本。您可以使用@DirtiesContext注解。

进行健全性检查是件好事,但您还应该编写一些测试来断言应用程序的行为。为此,您可以启动应用程序并侦听连接(就像在生产中一样),然后发送 HTTP 请求并断言响应。以下清单(来自 )显示了如何执行此操作:??src/test/java/com/example/testingweb/HttpRequestTest.java??

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”); }}

请注意,使用 的 使用随机端口启动服务器(有助于避免在测试环境中发生冲突)和注入带有 的端口。另外,请注意,Spring Boot 已自动为您提供一个。您所要做的就是添加它。??webEnvironment=RANDOM_PORT????@LocalServerPort????TestRestTemplate????@Autowired??

另一个有用的方法是根本不启动服务器,而只测试它下面的层,其中 Spring 处理传入的 HTTP 请求并将其交给您的控制器。这样,几乎使用了整个堆栈,并且您的代码将以与处理真实HTTP请求完全相同的方式调用,但没有启动服务器的成本。为此,请使用 Spring 的,并使用测试用例上的注释请求为您注入它。以下清单(来自 )显示了如何执行此操作:??MockMvc????@AutoConfigureMockMvc????src/test/java/com/example/testingweb/TestingWebApplicationTest.java??

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@AutoConfigureMockMvcpublic 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 应用程序上下文,但没有服务器。我们可以通过使用 将测试范围缩小到仅 Web 层,如以下列表(来自)所示:??@WebMvcTest????src/test/java/com/example/testingweb/WebLayerTest.java??

@WebMvcTestpublic 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”))); }}

测试断言与前一种情况相同。但是,在此测试中,Spring 引导仅实例化 Web 层,而不是整个上下文。在具有多个控制器的应用程序中,您甚至可以要求仅使用(例如)实例化一个控制器。??@WebMvcTest(HomeController.class)??

到目前为止,我们的很简单,没有依赖关系。我们可以通过引入一个额外的组件来存储问候语(也许在新控制器中)来使其更加逼真。以下示例(来自 )演示了如何执行此操作:??HomeController????src/main/java/com/example/testingweb/GreetingController.java??

package com.example.testingweb;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;@Controllerpublic class GreetingController { private final GreetingService service; public GreetingController(GreetingService service) { this.service = service; } @RequestMapping(“/greeting”) public @ResponseBody String greeting() { return service.greet(); }}

然后创建问候语服务,如以下列表(来自)所示:??src/main/java/com/example/testingweb/GreetingService.java??

package com.example.testingweb;import org.springframework.stereotype.Service;@Servicepublic class GreetingService { public String greet() { return “Hello, World”; }}

Spring 会自动将服务依赖注入控制器(因为构造函数签名)。下面的清单(来自)显示了如何使用以下命令测试此控制器:??src/test/java/com/example/testingweb/WebMockTest.java????@WebMvcTest??

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”))); }}

我们用于为 创建并注入模拟(如果不这样做,应用程序上下文将无法启动),并使用 .??@MockBean????GreetingService????Mockito??

总结

祝贺!您已经开发了一个 Spring 应用程序,并使用 JUnit 和 Spring 对其进行了测试,并使用 Spring Boot 来隔离 Web 层并加载特殊的应用程序上下文。??MockMvc??

要做一个积极勇敢乐观的追梦人,永远不说消极的话,

创建 Spring 应用程序然后使用 JUnit 对其进行测试

相关文章:

你感兴趣的文章:

标签云: