经过前面几次文章的分享的 UT 的相关知识,今天接着分享 UT 相关最后一测文章,希望对大家在 UT 的学习中有一点点的帮助。
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>
复制代码
为了与生产环境配置区分开
新建一个 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");
}
}
复制代码
参考
https://www.dropwizard.io/en/latest/manual/testing.html#
前文传送门
1、工作多年后我更了解了UT的重要性
2、五年了,你还在用junit4吗?
评论