一.前言知识
idea的单元测试插件-Squaretest :自动生成Mock单元测试
三-Junit5常用注解–大部分标记的是类
Junit5安装和运行
一.添加Junit5依赖
Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库;在 Spring Boot 2.2.0 版本之前,spring-boot-starter-test 包含了 JUnit 4 的依赖;
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
二.如何启用运行测试案例
方式一:执行项目路径下的所有测试用例
mvn test
方式二:采用maven的窗口
三.idea本身三种方式如何快速生成"目标类的各个方法"的测试方法–>本质上这三个都是一样的
方式一:右键类名(或目标方法名)–>Go to–>Test
方式二:鼠标停留在类名(或目标方法名)上–>快捷键Alt+Enter:生成"Control或者Service的测试类"
方式三:鼠标停留在类名(或目标方法名)上–>快捷键Alt+Insert
四.借助idea插件Squaretest :自动生成Mock单元测试
五.Idea自带覆盖率扫描工具的使用
六.jacoco插件覆盖率扫描工具的使用。
第一套:常规注解
@Test:表明一个测试方法
@Disabled:禁用测试类或者方法
@DisplayName:测试类或者方法的显示名称
@Tag—>对测试用例进行分组
@RepeatedTest:此目标方法额外重复执行
- 如图1.1标注,@DisplayName 可以定义显示类的名称,如图1.2标注,@DisplayName 可以定义显示方法的名称
- 如图2的标注,@Disabled ,该方法不执行,会看到方法名称前面有一个禁用的小图标
- 如图3的标注,@RepeatedTest 设置为2,看到该方法再执行了一次之后,又额外重复执行了2遍,所以该方法总共执行了3次
第二套:之前和之后
2.2、@BeforeAll
表明在所有测试方法执行之前执行的方法,且只执行一次
2.3、@AfterAll
表明在所有测试方法执行之后执行的方法,且只执行一次
2.4、@BeforeEach
表明在每个测试方法执行之前都会执行的方法
2.5、@AfterEach
表明在每个测试方法执行之后都会执行的风发
第三套:控制那些 狗xxText.java需要运行,进行跑测试案例
通过select来创建套件,通过包、类、和方法三个维度来选择需要执行的测试用例。所有条件是且的关系,求的是交集。
使用classname进行匹配时,需要加上所在的包名(唉~ 好麻烦)
若交集为空,如下图效果:
若交集不为空,则执行匹配的用例:
使用口诀:通过select来创建套件,通过包、类、和方法三个维度来选择需要执行的测试用例。
1.@RunWith(JUnitPlatform.class) -->执行套件
新建一个类TestRunWithDemo.java--.>作用就是制定哪些测试类,哪些测试方法运行跑测试案例
2.先创建"测试套件"的两个注解–>作用是扫描哪些测试类可以作为"测试套件"
@SelectPackage({“com.packageA”,“com.packageB”}) -->根据包-创建套件
@SelectClasses( {a.class,b.class,c.class} ) --.根据类-创建套件
3.过滤哪些测试包??
@IncludePackage(“包名”) -->在套件中,>过滤在所有的套件,获得需要执行的测试包
@ExcludePackages(“包名”) 在套件中,过滤剔除不需要执行的测试包
4.过滤哪些测试类??,即提前讲好哪些类不跑测试
@IncludeClassNamePatterns(“类名”) —>过滤在所有的套件,获得需要执行的测试类
@ExcludeClassNamePatterns(“类名”) —>在套件中,过滤剔除不需要执行的测试类
5.在套件中,过滤需要执行的测试方法
@IncludeTags(“tag”) -->在套件中,过滤在所有的套件,获得需要执行的测试方法
@ExcludeTags(“tag”) -->在套件中,过滤剔除不需要执行的测试方法
第五套:参数化–>主要是 @MethodSource 和@valueSource
注解 说明
|- @valueSource -| 通过注解可以直接指定携带的运行参数 |
| @EnumSource | 枚举参数元,允许我们通过将参数值有给定的Enum枚举类型传 |
| @MethodSource | 通过其他的Java方法函数来作为参数源 |
| @ArgumentSource | 参数类参数源,应用类必须实现ArgumentsProvide接口 |
| @CSVSource | csv格式作为参数源 |
| @CSVFileSource | csv文件作为参数源 |
以上是Junit5的参数化方式,在这里我就不一一介绍了,本人用的更多的MethodSource+yaml的参数化方式;因为yaml文档能够比较清晰地定义各种类型的参数,结合MethodSource注解就能够很方便的构建出不同类型的参数
简单的MethodSource实例
// 添加成员@ParameterizedTest
@MethodSource("testData")
public void successTest(String name,String phone) {
System.out.println("name: " + name +"phone: " + phone)
}
// 返回用例数据
public static Stream<Arguments> testData() {
return Stream.of(
Arguments.arguments("周杰伦","12121221211"),
Arguments.arguments("林俊杰","12121221212"),
Arguments.arguments("王力宏","12121221213"));
}
第四套:结果–>断言匹配
上面试Junit5自带的断言方式,如果需要更复杂的断言可以使用hamcrest断言方式;
软断言:如果一个用例需要进行多次断言,就需要用到assertAll(需要先设置java8语法),使用软断言后不会因为一个断言的失败报错而中断测试
软断言实例:
@Test
void addTest(){
assertAll(
()->assertEquals(9==9),
()->assertEquals(7==2),
()->assertEquals(10==10)
);
}
// 二:现将assert可执行语句进行储存,再用assertAll进行断言
ArrayList<Executable> assertList = new ArrayList<>();
for(int i = 0;i<3;i++){
int result = 1 + i;
assertList.add({
// 先将断言语句存起来
() -> assertEquals(3,result);
})
}
assertAll(assertList.Stream());
第六套:超时&&执行顺序&&重复
@Timeout(5)注解配置超时时间
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import static java.lang.Thread.sleep;
class TimeoutExampleTest {
@Test
@Timeout(7)
void timeoutDemo1() throws InterruptedException {
sleep(10000);
System.out.println("超时用例1");
}
@Test
void timeoutDemo2(){
System.out.println("超时用例2");
}
}
时间单位配置
可以结合注解@BeforeEach与@AfterEach
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import static java.lang.Thread.sleep;
class TimeoutExampleTest {
@AfterEach
@Timeout(3)
void teardown() throws InterruptedException {
sleep(40000);
System.out.println("结束测试");
}
@BeforeEach
@Timeout(3)
void setup() throws InterruptedException {
sleep(40000);
System.out.println("开始测试");
}
@Test
void timeoutDemo2(){
System.out.println("超时用例2");
}
}
@RepeatedTest:此目标方法额外重复执行
- 如图1.1标注,@DisplayName 可以定义显示类的名称,如图1.2标注,@DisplayName 可以定义显示方法的名称
- 如图2的标注,@Disabled ,该方法不执行,会看到方法名称前面有一个禁用的小图标
- 如图3的标注,@RepeatedTest 设置为2,看到该方法再执行了一次之后,又额外重复执行了2遍,所以该方法总共执行了3次
@Order()指定测试方法的执行顺序
使用场景–.集成测试(主流程测试)
测试用例有业务逻辑相关
集成测试(主流程测试)
方法排序
类型 说明
OrderAnnotation(重点) @Order 注解指定排序
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
//设定排序方法为注解指定排序
@TestMethodOrder(OrderAnnotation.class)
class OrderedTestsDemo {
@Test
@Order(1)
void nullValues() {
}
@Test
@Order(2)
void emptyValues() {
}
@Test
@Order(3)
void validValues() {
}
}
DisplayName 根据显示名称排序
package com.hogwarts.JUnit5;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
//根据DisplayName进行排序
@TestMethodOrder(MethodOrderer.DisplayName.class)
class orderExampleTest {
@Test
@DisplayName("a用例")
void firstCase() {
System.out.println("第一条用例");
}
@Test
@DisplayName("b用例")
void secondCase() {
System.out.println("第二条用例");
}
@Test
@DisplayName("c用例")
void thirdCase() {
System.out.println("第三条用例");
}
}
MethodName 根据方法名称排序
package com.hogwarts.JUnit5;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
// 设置排序类型为方法排序
@TestMethodOrder(MethodOrderer.MethodName.class)
class orderExampleTest {
@Test
void bCase() {
System.out.println("第二条用例");
}
@Test
void aCase() {
System.out.println("第一条用例");
}
@Test
void cCase() {
System.out.println("第三条用例");
}
}
Random 随机排序
package com.hogwarts.JUnit5;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
//设计排序类型为随机排序
@TestMethodOrder(MethodOrderer.Random.class)
class orderExampleTest {
@Test
void firstCase() {
System.out.println("第一条用例");
}
@Test
void secondCase() {
System.out.println("第二条用例");
}
@Test
void thirdCase() {
System.out.println("第三条用例");
}
}
类排序
类排序-DisplayName名称排序
package com.hogwarts.JUnit5;
import org.junit.jupiter.api.*;
// 指定类执行顺序的方式,DisplayName表示通过DisplayName排序
@TestClassOrder(ClassOrderer.DisplayName.class)
class orderExampleTest {
// 因为是多类执行,所以需要结合@Nested注解
@Nested
@DisplayName("b")
class PrimaryTests {
@Test
void test1() {
System.out.println("第一条用例");
}
}
@Nested
@DisplayName("a")
class SecondaryTests {
@Test
void test2() {
System.out.println("第二条用例");
}
}
}
类排序-Order 注解指定排序
import org.junit.jupiter.api.ClassOrderer;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestClassOrder;
// 指定类执行顺序的方式,OrderAnnotation表示通过order注解排序
@TestClassOrder(ClassOrderer.OrderAnnotation.class)
class OrderedNestedTestClassesDemo {
@Nested
@Order(1)
class PrimaryTests {
@Test
void test1() {
}
}
@Nested
@Order(2)
class SecondaryTests {
@Test
void test2() {
}
}
}
类排序-根据类名排序
package com.hogwarts.JUnit5;
import org.junit.jupiter.api.*;
// 指定类执行顺序的方式,ClassName代表指定为类名排序
@TestClassOrder(ClassOrderer.ClassName.class)
class orderExampleTest {
// 因为是多类执行,所以需要结合@Nested注解
@Nested
class PrimaryTests {
@Test
void test1() {
System.out.println("第一条用例");
}
}
@Nested
class SecondaryTests {
@Test
void test2() {
System.out.println("第二条用例");
}
}
@Nested
class ThirdTests {
@Test
void test2() {
System.out.println("第三条用例");
}
}
}
使用默认配置指定顺序
配置默认方法的执行顺序
#默认排序方式为随机排序
junit.jupiter.testmethod.order.default = org.junit.jupiter.api.MethodOrderer$Random
#默认排序方式为通过方法名排序
junit.jupiter.testmethod.order.default = org.junit.jupiter.api.MethodOrderer$MethodName
#默认排序方式为通过Order注解指定
junit.jupiter.testmethod.order.default = org.junit.jupiter.api.MethodOrderer$OrderAnnotation
#默认排序方式为通过DisplayName排序
junit.jupiter.testmethod.order.default = org.junit.jupiter.api.MethodOrderer$DisplayName
配置默认类的执行顺序
#默认排序方式为随机排序
junit.jupiter.testclass.order.default = org.junit.jupiter.api.ClassOrderer$Random
#默认排序方式为通过类名排序
junit.jupiter.testclass.order.default = org.junit.jupiter.api.ClassOrderer$ClassName
#默认排序方式为通过DisplayName排序
junit.jupiter.testclass.order.default = org.junit.jupiter.api.ClassOrderer$DisplayName
#默认排序方式为通过Order注解指定
junit.jupiter.testclass.order.default = org.junit.jupiter.api.ClassOrderer$OrderAnnotation
四.Mockito
参考文章一
参考文章二
参考文章三
参考文章四
参考文章五
引入依赖
<dependencies>
<!--导入Mockito单元测试依赖-->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<!--导入junit单元测试依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
五.高并发测压工具postman 和 jmeter的玩法
必须学的发送请求的三大工具:postman ,jmeter,curl命令
万神技之必备工具:postman模拟并发密集访问testB
1.postman里新建多线程集合组
2.将"目标访问地址"添加进新新线程组
3.Run运行Collection
大批量线程高并发访问B,导致A失效了
万神技之必备工具jmeter模拟并发密集访问testB
六.实战之快速进行SpringBoot项目进行单元测试
方式二:使用IDEA的自带的测试小图标参考视频一
简述使用
方式一:SpringBoot整合Junit进行单元测试:参考文章一 参考文章二 参考文章三 参考视频
1.创建test测试文件夹
2. 添加Junit的起步依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
3.更快速的使用生成测试类
方式一:"Junit插件"生成测试类
方式二:快捷键Alt+Enter:生成"Control或者Service的测试类"
4.编写测试类三个常用注解
基于Service层的测试
实战小例子
//"其中,SpringRunner继承自SpringJUnit4ClassRunner,使用哪一个Spring提供的测试测试引擎都可以"-->作用就是告示当前测试类我所使用的测试工具是谁?是"SpringRunner"
@RunWith(SpringRunner.class)
//"@SpringBootTest的属性指定的是引导类的字节码对象"-->用于启指向"主启动类"是谁,帮助你启动项目
@SpringBootTest(classes = MySpringBootApplication.class)
public class MapperTest {
@Autowired
private UserMapper userMapper;
@Resource
private UserService userService;
@Test public void test() {
List<User> users = userMapper.queryUserList();
System.out.println(users);
}
}
基于Control层的测试:采用一个MockMVC对象来实现
@SpringBootTest
class Test {
@Autowired
private WebApplicationContext webApplicationContext;
//mvc 环境对象
private MockMvc mockMvc;
@BeforeEach
public void init() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
} @Test
void getXhCreateTaskRecord() throws Exception {
// mvc = MockMvcBuilders.webAppContextSetup(wac).build();
final String json = "{}";
final MockHttpServletRequestBuilder request = MockMvcRequestBuilders.post("/getXhCreateTaskRecord")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON_VALUE)
//传json参数
.content(json);
final String contentAsString = mockMvc.perform(request).andReturn().getResponse().getContentAsString();
log.info(contentAsString);
}
}
"可以直接这样,不用使用 http://ip:port 了,这样测试更方便"
(选用)开启所有测试类的数据事务回滚
//配置事务的回滚,对数据库的增删改都会回滚,便于测试用例的循环利用
@Transactional
@Rollback
public class Test {
}
上面两句的作用是,让我们对数据库的操作会事务回滚,如对数据库的添加操作,在方法结束之后,会撤销我们对数据库的操作。
为什么要事务回滚?
测试过程对数据库的操作,会产生脏数据,影响我们数据的正确性
不方便循环测试,即假如这次我们将一个记录删除了,下次就无法再进行这个Junit测试了,因为该记录已经删除,将会报错。
如果不使用事务回滚,我们需要在代码中显式的对我们的增删改数据库操作进行恢复,将多很多和测试无关的代码
介绍如何使用"断言(Assert)语句 "判别测试结果。
使用Assert类的静态方法,assertEquals(),包含两个参数,左边是预期值,右边是实际值。当预期值与实际值不同时便会报错。
package com.junit.MyClass;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Created by lesley on 2017/1/14.
*/
public class CalculatorTest {
//注意这个不能忘记!!要不然后面无法调用
private Calculator calculator;
@Before
public void setUp() throws Exception {
calculator = new Calculator();
}
@Test
public void add() throws Exception {
assertEquals(calculator.add(3, 5),8);
}
}
package com.teachertom.test;
import com.teachertom.calculator.Calculator;
import org.junit.Assert;
import org.junit.Test;
public class CalculatorTest {
@Test
public void testAdd(){
Calculator calculator=new Calculator();
Assert.assertEquals(10,calculator.add(5,5));
}
@Test
public void testSubtract(){
Calculator calculator=new Calculator();
Assert.assertEquals(5,calculator.subtract(10,5));
}
}
点击运行,实际值与预期值相符,测试通过。
如果不相符,则会出现下列字样。
其他基本注解补充
2、Junit基本注解介绍
//在所有测试方法前执行一次,一般在其中写上整体初始化的代码
@BeforeClass
//在所有测试方法后执行一次,一般在其中写上销毁和释放资源的代码
@AfterClass
//在每个测试方法前执行,一般用来初始化方法(比如我们在测试别的方法时,类中与其他测试方法共享的值已经被改变,为了保证测试结果的有效性,我们会在@Before注解的方法中重置数据)
@Before
//在每个测试方法后执行,在方法执行完成后要做的事情
@After
// 测试方法执行超过1000毫秒后算超时,测试将失败
@Test(timeout = 1000)
// 测试方法期望得到的异常类,如果方法执行没有抛出指定的异常,则测试失败
@Test(expected = Exception.class)
// 执行测试时将忽略掉此方法,如果用于修饰类,则忽略整个类
@Ignore("not ready yet")
@Test
@RunWith
在JUnit中有很多个Runner,他们负责调用你的测试代码,每一个Runner都有各自的特殊功能,你要根据需要选择不同的Runner来运行你的测试代码。
如果我们只是简单的做普通Java测试,不涉及Spring Web项目,你可以省略@RunWith注解,这样系统会自动使用默认Runner来运行你的代码。
5、如何运行一堆"测试类"
方式二:看图如下
方式一:新建一个测试类–>用于所有测试类
正常情况下我们写了5个测试类,我们需要一个一个执行。
打包测试,就是新增一个类,然后将我们写好的其他测试类配置在一起,然后直接运行这个类就达到同时运行其他几个测试的目的。
@RunWith(Suite.class)
@SuiteClasses({ATest.class, BTest.class, CTest.class})
public class ABCSuite {
// 类中不需要编写代码
}