文章目录
Junit5测试入门
@Test注解 用于执行每一个测试用例必须在前方写的注解
测试的四个生命周期:
@BeforeAll、@AfterAll、@BeforeEach、@AfterEach
@BeforeAll和@AfterAll修饰的都是静态方法(static)
执行顺序:
所有测试用例前执行@BeforeAll
所有测试用例后执行@AfterAll
每个测试用例前执行@BeforeEach
每个测试用例后执行@AfterEach
含有继承关系的执行顺序:
1.先执行父操作,再执行子操作
2.先执行的后结束
断言方法
在调用断言方法的时候,如果不能通过快捷键导入类,就导入下面类
import static org.junit.jupiter.api.Assertions.*;
setUp和tearDown
@BeforeEach 前置条件,在每条用例前执行
@BeforeEach
void setUp(){
System.out.println("在每条用例执行之前的前置动作");
}
@AfterEach 后置动作,在每条用例后执行
@AfterEach
void tearDown(){
System.out.println("在每条用例之后执行的后置动作");
}
assertEquals 断言相等
assertTure 断言为True
@Test
void assertTrueDemo(){
System.out.println("断言表达式为Ture");
assertTrue(3>1);
}
assertNotNull 断言非空
assertAll 断言所有
@Test
void assertAllDemo(){
//第一个参数heading可以传描述信息,也可以不写
assertAll("all",
()->assertEquals(2,1+1),
()->assertEquals(3,1+1),
()->assertEquals(4,1+1)
);
}
通过匿名函数来进行多个断言方法,会依次断言所有的结果,不会因为断言为false而终止断言
org.opentest4j.AssertionFailedError:
Expected :3
Actual :2
<Click to see difference>
org.opentest4j.AssertionFailedError:
Expected :4
Actual :2
<Click to see difference>
assertTimeout
@Test
void assertTimeOutDemo(){
assertTimeout(Duration.ofSeconds(3),() -> {sleep(4000);}); //匿名箭头函数
}
第一个参数传递超时时间
第二个参数传递操作
如果第二个参数>第一个参数超时时间 -> 会返回报错,反之通过
assertThrows
void fn(int a, int b){
System.out.println(a/b);
}
@Test
void assertThrowsDemo(){
//异常断言
assertThrows(ArithmeticException.class,() -> fn(1,0));
}
如果断言异常相同通过,没有抛出异常会报错
Junit5测试用例调度与运行
执行测试类
mvn test -Dtest = 包名.类名
在Pom里配置执行类
<include>包名/类名</include> //要执行的测试类
<exclude>包名/类名</exclude> //不执行的类名
<include>包名/*Test</include> //使用正则匹配执行的类
执行测试类中的方法
mvn test -Dtest = 包名.类名#方法名1 + 方法名2
Junit5参数化与调度执行
尽量不要将@Test注解和@ValueSource结合使用,如果使用,用例会多执行一次
单参数注解@ValueSource
@ParameterizedTest
@ValueSource(strings = {"张三","李四","王五"})
void ValueSourceDemo(String name){ //在测试方法上要添加形参,用来接收参数化的数据
System.out.println(name);
}
@ParameterizedTest -> 声明测试类是一个参数化的测试类(将@Test换成@ParameterizedTest)只要你是参数化,必须 加上此注解
@ValueSource -> 使用单参数注解@ValueSource传递参数化的数据内容,传递过程中,需要通过ValueSource定义 的关键字进行类型声明
但很多场景并不会只有一个参数进行参数化的验证,所以就需要使用多参数的参数化方式
以下就开始介绍多参数的参数化的使用:
多参数参数化注解@CsvSource
@ParameterizedTest
@CsvSource({"张三,3","李四,4","王五,15"})
void csvSourceDemoATest(String name,Integer age){
assertEquals(name.length(),2);
assertTrue(age > 2);
}
@ParameterizedTest -> 声明测试类是一个参数化的测试类
@CsvSource -> 指定数据源是Csv数据(Csv默认分隔符为,)
也可以指定分隔符,例如,我想把分隔符用|显示:
@ParameterizedTest
@CsvSource(value = {"张三|3","李四|4","王五|15"},delimiterString = "|")
void csvSourceDemoBTest(String name,Integer age){
assertEquals(name.length(),2);
assertTrue(age > 2);
}
@ParameterizedTest -> 声明测试类是一个参数化的测试类
@CsvSource -> 指定数据源是Csv数据(指定分隔符为|)
value -> 指定数据原
delimiterString -> 自定义分隔符
当然,日常工作中肯定还要有读取csv文件内容进行测试的
举例说明:
这里先在Java同级目录下建立了resources配置文件,然后在该配置文件下面建立了data.csv文件,内容如下:
张三,3
李四,15
王五,18
创建测试类,实现文件的调用
@ParameterizedTest
@CsvFileSource(resources = "/data.csv")
void csvFileSourceADemo(String name,Integer age){
System.out.println(name + "的年龄是:" +age);
}
@ParameterizedTest -> 声明测试类是一个参数化的测试类
@CsvFileSource -> 声明参数化数据是File来源
resources -> 使用关键字resources指定文件,注意:文件名前面要加/
与此同时,因为上方默认是逗号分割的csv文件,所以,想要改变默认分隔符需要像下方代码。
我们新建一份data2.csv文件放入resources配置文件内,内容如下:
元歌|20
李信|4
司空震|8
创建测试类,实现文件的调用
@ParameterizedTest
@CsvFileSource(resources = "/data2.csv",delimiterString = "|")
void csvFileSourceBDemo(String name,Integer age){
System.out.println(name + "的年龄是:" +age);
}
delimiterString -> 自定义分隔符
单参数@MethodSource参数化
@MethodSource传入方法名称
@ParameterizedTest
@MethodSource("stringProvider")
void methodSourceDemo(String name){
System.out.println(name);
}
//定义静态方法,提供参数化数据源
static Stream<String> stringProvider(){
return Stream.of("苹果","菠萝");
}
@ParameterizedTest -> 声明测试类是一个参数化的测试类
@MethodSource -> 通过MethodSource指定数据源的方法
注意:
1、通过@MethodSource指定的数据源方法要和下方静态方法的方法名一致
2、测试方法中添加形参的类型需要和下方静态方法中内部的元素类型一致
还可以不传入方法名,IDEA会直接引用同名的方法
@ParameterizedTest
@MethodSource
void methodSourceDemo(String name){
System.out.println(name);
}
static Stream<String> methodSourceDemo(){
return Stream.of("芒果","菠萝");
}
如果不加参数,会自动找和测试方法同名的静态方法!!
以上就是单参数的场景应用,所以既然有单参数那也肯定有多参数场景的存在
多参数@MethodSource参数化
@MethodSource传入方法名称
@ParameterizedTest
@MethodSource("objectProvider")
void multiMethodSourceDemo(String name,Integer age){
System.out.println(name + "的年龄是:" +age);
}
static Stream<Arguments> objectProvider(){
return Stream.of(
Arguments.arguments("张三",10),
Arguments.arguments("李四",15)
);
}
与上面单参数是大体一致的,这里就不过多解释了
同样的,也可以实现不传方法名自动引用同名方法:
@ParameterizedTest
@MethodSource
void multiMethodSourceDemo(String name,Integer age){
System.out.println(name + "的年龄是:" +age);
}
static Stream<Arguments> multiMethodSourceDemo(){
return Stream.of(
Arguments.arguments("王五",10),
Arguments.arguments("老六",15)
);
}
因为形参的变多,而且不再是单一的数据类型,所以junit5中提供了内置接口Arguments,也是官方推荐的用法
与单参数的大体一致,但有差异:
1、形参变多了
2、返回的不再是Stream<基本数据类型>,而是Stream(arguments(数据信息))
枚举参数化注解—简单使用
使用枚举类作为测试数据
枚举参数参数化注解@EnumSource
必须与@ParameterizedTest结合使用
public enum Temp{
xiaoma("xiaoma",12),
str1("str1",18),
str2("str2",20);
private final String name;
private final Integer age;
private Temp(String name,Integer age){
this.name = name;
this.age = age;
}
}
@ParameterizedTest
@EnumSource(mode = EnumSource.Mode.EXCLUDE, names = {"str1","str2"})
void enumSourceDemo(Temp temp){
System.out.println(temp.name + "的年龄是:" + temp.age);
}
@ParameterizedTest -> 声明测试类是一个参数化的测试类
@EnumSource -> 声明通过枚举类作为数据来源
mode -> 参数指定,EXCLUDE表示取反,此代码里表示不测试str1和str2
names -> 通过names参数指定枚举值,也就是选定哪些数据进行测试
将上方@EnumSource注解注释掉重新写:
public enum Temp{
xiaoma("xiaoma",12),
str1("str1",18),
str2("str2",20);
private final String name;
private final Integer age;
private Temp(String name,Integer age){
this.name = name;
this.age = age;
}
}
@ParameterizedTest
// @EnumSource(mode = EnumSource.Mode.EXCLUDE, names = {"str1","str2"})
@EnumSource(mode = EnumSource.Mode.MATCH_ALL, names = "str.*")
void enumSourceDemo(Temp temp){
System.out.println(temp.name + "的年龄是:" + temp.age);
}
意思是使用正则表达式,匹配数据信息里str开头的所有数据进行测试,所以只会运行str1和str2
junit5中特殊参数的参数化
应用场景不多,了解一下就行
@NullSource、@EmptySource、@NullAndEmptySource、@ValueSource
@ParameterizedTest
@NullSource
void nullSourceDemo(String param){
System.out.println(param);
}
@ParameterizedTest
@EmptySource
void emptySourceDemo(String param){
System.out.println(param);
}
@ParameterizedTest
@NullAndEmptySource
void nullAndEmptySourceDemo(String param){
System.out.println(param);
}
@NullSource -> null参数的参数化注解
@EmptySource -> 参数为空的参数化注解
@NullAndEmptySource -> null和空都进行参数化
@ValueSource -> 如果还有其他参数,用该注解
Junit5超时处理
在测试过程中,如果某条测试用例执行过程发生堵塞,就会发生超时问题
发生超时:1、会堵塞其他用例的执行
2、对于有些用例执行时间过长,那本身就是个BUG
超时处理使用@TimeOut注解,如果执行的时间超过了人为设定的@TimeOut的时间,就会报错。
举例说明:
@Test
@Timeout(3)
void timeOutCaseOne() throws InterruptedException {
System.out.println("第一条用例");
}
@Test
@Timeout(3)
void timeOutCaseTwo() throws InterruptedException {
sleep(8000); //8000毫秒,转换过来就是8秒,1000为一秒
System.out.println("第二条用例");
}
在这里我们定义了两条测试用例,都为其添加上了@TimeOut注解,然后在第二条用例上故意让它sleep睡眠8秒
注意一点差异:
@TimeOut注解后传参是以秒作为单位,而sleep()方法内传参是以毫秒为单位,1000毫秒为一秒
所以最后测试结果会在第二条测试用例报错超出了超时时间
当然,@TimeOut注解的单位也可以自己来定义:
@Test
@Timeout(value = 30,unit = TimeUnit.MILLISECONDS)
void timeOutCaseThree() throws InterruptedException {
sleep(31);
System.out.println("第一条用例");
}
value -> 为其设置超时时间
unit -> 设置时间单位,默认就是SECONDS(秒)
时间单位配置表:
30毫秒 @Timeout(value = 30,unit = TimeUnit.MILLISECONDS)
30秒 @Timeout(value = 30,unit = TimeUnit.SECONDS)
30分 @Timeout(value = 30,unit = TimeUnit.MINUTES)
30小时 @Timeout(value = 30,unit = TimeUnit.HOURS)
30天 @Timeout(value = 30,unit = TimeUnit.DAYS)
Junit显示名称(别名、生成器配置、配置文件全局配置)
别名:为测试用例添加一个显示名称,覆盖原有名称,更有利于识别
@DisplayName("测试类")
public class DisplayNameDemoTest {
@Test
@DisplayName("第一条测试用例")
void testCaseOne(){
System.out.println("测试用例1");
}
@Test
@DisplayName("第二条测试用例")
void testCaseTwo(){
System.out.println("测试用例2");
}
}
@DisplayName适用于方法和类
生成器配置:
Standard -> 默认配置 包含括号,例如:testCaseOne() -> testCaseOne()
Simple -> 删除没有参数方法的尾括号 不包含括号,例如:testCaseOne() -> testCaseOne
ReplaceUnderscores -> 用空格替换下划线 例如:testCase_One() -> testCase One
IndicativeSentences -> 使用类名+方法名 例如:testCaseOne() -> DisplayNameDemoTest,testCaseOne
代码展示:
@DisplayNameGeneration(DisplayNameGenerator.Standard.class)
@Test
void testCase_One(){
System.out.println("测试用例1");
}
@Test
void testCase_Two(){
System.out.println("测试用例2");
}
如图所示,也就是默认的情况,方法名怎么写就怎么显示
@DisplayNameGeneration(DisplayNameGenerator.Simple.class)
同样的代码,注解换成Simple会删除掉没有参数的方法名
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
使用ReplaceUnderscores会替换掉原_,替换为空格
@DisplayNameGeneration(DisplayNameGenerator.IndicativeSentences.class)
使用IndicativeSentences会在方法名前加上类名,更加方便识别具体来源哪个类中哪个方法,类和方法中间是用,隔开
## 创建配置文件进行全局配置
代码展示:
执行结果:
在这里我们可以看到,已经注释掉了所有的生成器配置信息,但结果依旧存在生成器的格式,并不是根据默认情况执行
原因其实很简单,只需要在测试用例Java同级目录下添加个resources包里新建一个properties文件,在这个配置文件里进行全局配置:
junit.jupiter.displayname.generator.default = \
org.junit.jupiter.api.DisplayNameGenerator$IndicativeSentences
同样的,这个代码实现的是IndicativeSentences功能,即类名,方法名的格式
除此之外想要用其他生成器配置属性改动 后的内容即可,例如 后的内容即可,例如 后的内容即可,例如ReplaceUnderscores
Junit5嵌套测试
待更新……关注一下吧!~~~~