Spring集成测试
有时候我们需要在跑起来的Spring环境中验证,Spring 框架提供了一个专门的测试模块(spring-test),用于应用程序的集成测试。
在 Spring Boot 中,你可以通过spring-boot-starter-test启动器快速开启和使用它。
这时首先就有了Spring容器运行环境,就可以模拟浏览器调用等操作
引入测试坐标
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.19.0</version> <scope>test</scope> </dependency>
为了与生产环境配置区分开,一般都是配置多个配置文件,这样不同场景就能使用不同的配置文件
比如划分成-prod为生产环境,-dev为开发环境
新建一个application-test.yml
server: port: 8088 spring: application: name: hello-service-for-test
controller类,也就是被测对象
@RestController public class HelloController { @RequestMapping(value = "/hello", method = RequestMethod.GET) public String hello() throws Exception { return "Hello World"; } }
测试方案一
通过TestRestTemplate模拟调用Rest接口
@ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("test") class HelloControllerTest { @Autowired private TestRestTemplate restTemplate; @Value("${spring.application.name}") private String appName; @BeforeEach void setUp() { assertThat(appName).isEqualTo("hello-service-for-test"); } @Test void testHello() throws Exception { String response = restTemplate.getForObject("/hello", String.class); assertThat(response).isEqualTo("Hello World"); } }
测试方案二
通过MockMvc来调用Rest接口
@ExtendWith(SpringExtension.class) @SpringBootTest @AutoConfigureMockMvc @ActiveProfiles("test") class HelloControllerTest { @Autowired private HelloController helloController; @Autowired private MockMvc mockMvc; @Value("${spring.application.name}") private String appName; @BeforeEach void setUp() { assertThat(appName).isEqualTo("hello-service-for-test"); } @Test void testHello() throws Exception { mockMvc.perform(MockMvcRequestBuilders.get("/hello")) .andDo(MockMvcResultHandlers.print()) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.content().string("Hello World")); } }
方案一会启动Spring容器,相对更符合我们测试思路,建议选用此方案测试
方案二不会启动内置的容器,所以耗时相对少一点
与Spring类似dropwizard也有一套测试方案,可以提供Jetty容器来做集成测试
Dropwizard集成测试
引入maven坐标
<dependency> <groupId>io.dropwizard</groupId> <artifactId>dropwizard-testing</artifactId> <version>2.0.21</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.6.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.19.0</version> <scope>test</scope> </dependency>
被测试资源类
@Path("/ping") @Service public class PingResource { @GET public String ping() { return "pong"; } }
测试方案一
不启动Jetty容器,通过ResourceExtension扩展测试
@ExtendWith(DropwizardExtensionsSupport.class) class PingResourceTest { private static final ResourceExtension EXT = ResourceExtension.builder() .addResource(new PingResource()) .build(); @Test void should_get_pong_when_send_ping() { String response = EXT.target("/ping").request().get(String.class); assertThat(response).isEqualTo("pong"); } }
测试方案二
通过启动Jetty容器测试,为了避免项目中的循环依赖关系或加快测试运行速度,可以通过将JAX-RS资源编写为测试DropwizardClientExtension
来测试HTTP客户端代码,并启动和停止包含测试的简单Dropwizard应用程序。
@ExtendWith(DropwizardExtensionsSupport.class) class PingResourceTest3 { private static final DropwizardClientExtension EXT = new DropwizardClientExtension(new PingResource()); @Test void should_get_pong_when_send_ping() throws IOException { URL url = new URL(EXT.baseUri() + "/ping"); System.out.println(url.toString()); String response = new BufferedReader(new InputStreamReader(url.openStream())).readLine(); assertThat(response).isEqualTo("pong"); } } 复制代码
测试方案三
通过指定yml配置文件,Jersey HTTP client调用Rest接口, 返回的客户端可以在测试之间重用
在JUnit5测试类中添加DropwizardExtensionsSupport
注释和DropwizardAppExtension
扩展名将在运行任何测试之前启动应用程序
并在测试完成后再次停止运行(大致等同于使用@BeforeAll
和@AfterAll
)
DropwizardAppExtension
也暴露了应用程序的Configuration
, Environment
并且应用程序对象本身,使这些可以通过测试进行查询。
新增配置文件\src\test\resources\hello.yml
server: type: simple rootPath: '/api/*' applicationContextPath: / connector: type: http port: 9090 复制代码
测试:
@ExtendWith(DropwizardExtensionsSupport.class) class PingResourceTest2 { private static DropwizardAppExtension<HelloWorldServiceConfiguration> EXT = new DropwizardAppExtension<>( HelloWorldServiceApp.class, ResourceHelpers.resourceFilePath("hello.yml") ); @Test void should_get_pong_when_send_ping() { Client client = EXT.client(); String response = client.target( String.format("http://localhost:%d/api/ping", EXT.getLocalPort())) .request() .get(String.class); assertThat(response).isEqualTo("pong"); } }