有啥不同?来看看Spring Boot 基于 JUnit 5 实现单元测试

云栖号资讯:【点击查看更多行业资讯】
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!

简介

Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库,在 Spring Boot 2.2.0 版本之前,spring-boot-star9 J C 0 x $ter-teD ? e Q * ; L ast 包含了 JUnit 4 的依赖,Spring Boot 2.2.0 版本之后替换成了 Junit Jupiter。

JUnit 4 和 JUnit 5 的差异

  1. 忽略测试用例G A = Z w执行

JUnit 4:

@Test
@Ignore
public$  5 ? { ] void testMe: 8 K I j n ) 6 |thod() {
// ...
}

JUnit 5:

@Test
@Disabled("explanation")
public void testMethod() {
// ...
}
  1. RunWith 配置

JUnit 4:

@Ru` C ] 7nWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {
@Test
public void contextLoads() {
}
}

JR f % cU Onit 5:

@ExtendWith(SpringExtension.class)
@SpringBootTest
publij , 8c class ApplicationTests {
@Test
pub c C =lic void context @ VtLoads() {
}
}
  1. @Before、@BeforeClass、@After、@AfterClass 被替换

@BeforeEach 替换 @Before
@BeforeAll 替换 @BeforeClass
@AfterEach 替换 @Afte# 7 / $ * 7 ^r
@AfterAll 替换 @AfterClass

开发环境

JDK 8

示例

1.创建 SprF s V F M B Ming Boot 工程。

2.添加 spw ] J l _ J %ring-boot-starter-web 依赖,最终 pG ? J a Xom.xml 如下。

<?xml versi| 2 / N & B ] V ]on="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apa. R , { 7 X 6 $ zche.x Z p K l o Q Yorg/7 s ^ 5POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativey Q A q * SPaV P  R *th/>
</parent>
<groupId>tutori% g . Yal.spring.boot</groupId>
<artifactId>spring-boot-junit5</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-bu ; A =ooti V T I a g &-junit! B ) R , - $ /5</name>
<description>Demo projr C ) F v h l } 1ect for Spring Boot Unit Test with JUnit 5</descript0 * V Aion>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webV C / H ! 1 , ) u</artifactId>
</dependency>
<U S K u ~ 6 .dependency>
<groupId>org.springframework.boX ( = ? ot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclF d N X - & K Xusion>
<^ @ P ugroupId>org.junit.vintage</groupId>
<artifactId>t n !;junit-vintage-engine</aru ( 5tifactId>z i v + 5 a 4 5 (
</exclusion>
</excf I k ~ 9 I   Rlusions>
<~ % P V 2 0 b/dependency>
</dependencies>
&` g | N i G ~ olt;build>
<plugins>
<plugin&gW  c L 7t;
<groupId>org.sp? P * @ringframework.boot</gr/ + ! H ; y a ZoupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>y 6 S B r 9 ?;

3.1 k P U ` K ]工程创建好之后自动生成了一个测试类。

package tutorial.spring.boot.junit5;
import org.junb . k z b jit.jupiter.api.Test;
importo z / F org.spri2 O 4 _ / & ingframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringBootJunit5ApplicationTests {
@T1 b 2 ) |est
void contextLoads() {
}
}

这个测试类的作用是检查应用程序上下文是否可正常启动。@SpringBootTest 注解y ^ W 9 , % O告诉 Spring Boot 查找带 @SpringBootApplication 注解的主配置类,并使用该类启动 Spring 应用程序上下文。互联网架构师公众号内回复“2T”, 送你全套Javad J O T X - R _ Y架构视频

4.补充待测试应用@ M b D z w _逻辑代码

4.1. 定义 Service 层接口

package tue + # x Utorial.spring.boot.junit5.service;
public interface Helf f K loServica x ^ z g , se {
String hello(String name);
}

4.2. 定义 Controller 层

package tutorial.spring.boot.junit5.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.ani : o enotation.PathVariable;
import org.springframework.web.bind.annotation.U } / s oRestController;v l c 6 h N T
impo7 l Y g hrt tutorial.spring.book 6 | Jt.junit5.service.HelloService;
@RestController
pu7 E _blic class HelloController {
private fiM | y ? 8nal HelloSerO 0 yvice helloServR  $ )ice;
public Helloz e LController(HelloServicen w ( 0 f helloService) {
th] f 2 `is.helloService = helloService;
}
@GetMapping("/hello/{name}")
public String hello(@PathVariable("name") String name) {
return hellX G AoService.h3 ^ z # ne2 P 5 q llo(name);
}
}

4.3. 定义 Service 层实现

package tutorial.spring.boot.junit5.service.impl;
import org.springfram5 j pework.stereok R l P b Z / 8 ^tyf m k i % # ~ _pe.Service;
import tutorial.spring.boot.junit5.service.HelloService;
@Service
public class HelloServiceImpl implementsF - n 4 : e Hello: N 4 [ 1 ,Service {
@Override
public String hello(String n2 $ x )ame) {
return "Hello, " + name;
}
}

5.编写发送 HTTP 请求的单元测n ] | [ j , |试。

package tutorial.spring.boot.junit5;
import org.assertj.core.@ 9 [ 1 k 5api.Asserv  h Y w q z tions;
import org.junit.ju3 7 D ; R a ) Spiter.api.Test;
import org.springframework.beans.facto4 ` R % { N wry.annotation.Autowired;
import org.springframework.boot& n .test.context.SpringBootTestZ n g ? + ^ M Q;
import org.springframeworkD z K.boot.test.web.client.TestRestTemplate;
import org.sprinf q ` , Xgframework.boot.web. n i 6 1.ser[ M |v^ 3 e o x Uer.LocalServerPort;
@SpringBootTest(webEnvironmenk , D .t = SpringBootTest.WebEnvR R 9 s p y U a iironment.RANDOM_PORT)
public class HttpRequestTest {
@LocalServer_ $ b + 1 s .Port
private inT F 1 8 , Vt port;
@Autowired
private TestRestTemplate restTempl_ _ O 8 Y * y ate;
@Test
public void testHello() {
String requestResult = this.restTemQ / i P C & O rplate.getForObject("http://127.0.0.1:" + port + "/hello/spring",
String.class);
Assertions.g T y - j O _ n XassertThat(requestReM O $ ( 9 z . ^ Osult).conta^ K 8 R : K hins("Hello,} J ) 2 1 spring4 C m s m L");
}
}

说明:

webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT 使用本地的一个随机端口启动服务;

@Z | G vLocalServerPort 相当于 @Value("${local.server.port}");

在配置了 webEnvironment 后,Spring Boot 会自动提供一个 Tl * n 7 X 8 ; q -estRestTemplate 实例,可用于发送 HTTP 请求。

除了使用 TestRestTemplate 实例发送 HTTP 请求外,还可以借助 org.springfraQ & 9 i dmewI } 3 | Eork.test.web.servlet.MockMvc 完成类似功能,代码如下:

package tutorial.spring.w l , 6 + )boot.junit5.controller;
import org.assertj.core.api.Assertions;
import ol ! .rg.junit.jup+ d ` & v |iter.api.Test;
import org.springfrc J K D e  p (ameb V O Q [ a -work.beans.factory.annotation.Autowired;
impH o e n ^ U 7 = 3ort org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.tesd 0 $ _ ; Dt.context.SpringBootTest;
import org.sprinD * 3 w ! @ A % Ggframework.testu I 8 # S.web.servlet.MockMvc;
import org.sprb  | ` { z n Jingframework.test.web.servlet.request.MockMvcReques! y 9 h +tBuilders;
import org.springframework.test.web.ser) Y { E z . X  lvlet.r_ % g sesult.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultM* E L f v S 4 atchers;
@SpringBootTest
@AutoConfigl P 3 S D 6 jureMockMvc
public clas[ o L a p ks HelloControllerTest {
@Autowir[ x # Jed
private HellH = y joController helloController;
@Autowired
private MockMvc mockMvc;
@Test
public void testNotNull() {
Assertions.assertThat(he8 Y  GlloController).isNotNu+ A  P }ll();
}
@Test
public void testHello() throws Exception {
this.mockMvc.perform(MockMvcRequestBuilders.get("/hello/spring"))
.andDo(MockMvcResultHandlers.prink E qt())
.andExpect(MockMvcResultMatc+ z h 5 $ 5 4 Dhers.status().isOk())
.andExpect(MockMvcResultMatchers.contn - $ bent().string("Hello, spring"));
}
}

以上测试方法A # / r S属于整体测试,即将应用上下文全都启动起来,还有一种分层测试方L Q / s H法,譬如仅测试 Co0 _ 1 F v p i 0ntn U l c 6 Q n ]roller 层。

6.分层测试。

package tutorial.spring.boot.junit5.controller;
import org.assertj.core.2 S a [ { 6 g . }api.Assertions;
import org.junit.jupit} D yer.api.Test;
impf A #ort org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.bu G . `  Foot.test.mock.mockitoO e t ? , Q.MockBean;
import orgR  t S p 3 V S.springframewz F Q 1 = W `ork.test.web.servlet.M! Y ! !ockU A , L v B | v yMvc;
importE r k 2 = org.springframework.test.web.servlet.request.Mo7 # V = ~ T B :ckz ) U , * ~ , DMvcRequestBuilders;
import org.springframework.test.web.servlet.result.Mock+ i q K % o v * 6MvcResultHandlers;
import orgZ u d.springframework.test.web.servlet.result.MockMvcResultMa[ { 9 ctchers;
import tutorial.spring.boot.junit5.service.HelloServy T ! + P O v k 4ice;
@WebMvcTest
public class HelloCW { 9 6 . k s ,ontW 9 `rollerTest {
@AutowirE U Z * wedF ( ] c
private HelloController helloController;
@Autowired
privateO R ` V , MockMvc m5 y & w UockMvc;
@MockBean
privatE 7 Je HelloService helloService;
@Test
public void testNotNull() {
Assertions.assertThat(helloController).isNotNull(! b v W , T e d G);
}
@Test
public void testHello() throws Exception {
Mockito.when(helloService.hello(Mockito.anyString())).thenReturn(g o j n : I j"Mock hello");v o b
this.mockM? n I = % 2 {vc.perform(MockMvcRequestBuilders.get("/hello/spriv z G $ k Sng"))
.andDo(MockMvcResultHandlers.print())
.andExpect(MT U I F `ockMvcResultMatchem S T O * 6 b 7rs.status().isOk())
.andExpect(MW + % H j u l oockMvcResultMatchers.content().string("o x U . g % : g ?Mock hello"));
}
}

说明:

@0 b 0 @ n {WebMvcTest 注释告诉 Spring Bootb 9 M ^ 1 仅实例化 ControY O ) q dller 层,而不去实例化整体上下文,还可以进一步指定Q C X #仅实例化 Controller 层的某个实例:@WebMvcTest(Hellf n I % ,oController.claa 3 R nss);

因为只实例化s ) 2 # + 5 ] =了 Controller 层,所以依赖的 Service 层实例需要通过 @MockBean 创建,并通过 Mockito 的方法指定 Mock 出来的 Service 层实例在特定情况下方法调用时的返回结果。

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:httpsS k 1 ;://yqh.aliyun.com/zhibo

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-08-04
本文作者:又语
本文来自:“互联网架构师”,了解相关信息可以关注“互联网架构师”