前言
我们对自己的项目实施Web自动化测试使用的是selenium脚本实现的,而junit就是一个强大的单元测试工具,让测试更加完整全面,接下来我们就来学习一下junit的强大功能吧~
1.前置工作
首先因该先在我们的项目中引入junit5的依赖,如下:
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite</artifactId>
<version>1.8.2</version>
<scope>test</scope>
</dependency>
注意:
a)junit5支持的java版本最低为8;
b)如果之前用过junit,例如junit4,现在下载junit5,可能会下载失败,所以最好找到之前下载好的junit4依赖,删除之后再重新下载junit5。
2、注解
junit中提供了非常强大的注解功能,想要很好的测试自己的项目,我们需要知道以下五个注解:
1.@Test 是作用在方法上的,表示该方法时测试方法,执行当前这个类时,会自动的执行该类下所有带@Test注解的用例。
例如,创建一个Test1类,再这个类里创建两个方法aaa和bbb,只有aaa方法有@Test注解,那么就只会执行aaa方法,代码如下:
public class Test1 {
@Test
public void aaa() {
System.out.println("执行了aaa方法");
}
public void bbb() {
System.out.println("执行了bbb方法");
}
}
执行结果如下:
2.@BeforeEach 是作用在方法上的,表示当前方法需要再每一个用例执行之前都执行一次。
例如,当前类下有两个测试用例(aaa和bbb方法),还有一个被@BeforeEach修饰的方法(ccc方法),那么当程序运行起来时,执行的顺序就是 ccc -> aaa -> ccc -> bbb ,如下代码:
class Test2 {
@Test
public void aaa() {
System.out.println("执行了aaa方法");
}
@Test
public void bbb() {
System.out.println("执行了bbb方法");
}
@BeforeEach
public void ccc() {
System.out.println("执行了ccc方法");
}
}
执行结果如下:
3.@BeforeAll 是作用在方法上的,表示当前方法需要在当前类下所有用例执行之前执行一次,并且被该注解修饰的方法必须是静态方法。
例如:当前类下有两个用例aaa和bbb方法,还有一个被@BeforeAll修饰的静态方法ccc,那么当程序运行起来时,就会先执行ccc方法,在执行aaa和bbb方法,如下代码:
class Test3 {
@Test
public void aaa() {
System.out.println("执行了aaa方法");
}
@Test
public void bbb() {
System.out.println("执行了bbb方法");
}
@BeforeAll
public static void ccc() {
System.out.println("执行了ccc方法");
}
}
执行结果如下:
4.@AfterEach 的功能和 @BeforeEach 相反,也就是说表示当前方法需要每个用例执行之后都执行一次。
@AfterAll 的功能和 @BeforeAll 相反,也就是说表示当前方法在当前类下的所有用例执行完了以后执行一次,并且被该注解修饰的方法必须是静态的。
2、断言(Assertions类)
2.1、断言 匹配/不匹配
assertEquals:有两个参数(参数支持很多种类型,例如Java的基本类型,还有很多这里就不一一列举了),第一个参数是我们期望的结果,第二个参数是实际的结果,此方法表示如果期望结果和实际结果一致就继续执行下一条指令,若不一致,就会打印如下日志:
上图就是比较 期望结果1,和实际结果2,代码如下:
@Test
public void test() {
Assertions.assertEquals(1, 2);
}
那么,不难理解,asserNotEquals 和 assertEquals 表示意思正好相反,也就是说 asserNotEquals 当期望结果和实际结果符合时,就会报错并打印日志。
2.2、断言结果 为真/为假
assertTrue:有一个boolean类型的参数(状态),就像一个if语句一样,用来判断所给的状态是否为true,若为true则程序继续正常运行,若为false则打印如下日志:
上图的状态就是比较1 == 2,代码如下:
@Test
public void test2() {
Assertions.assertTrue(1 == 2);
}
那么不难理解,assertFalse 就表示当状态的结果为true时,程序就会报错并打印日志。
2.3、断言结果 为空/不为空
assertNull:有一个Object(这里讲的是常用用法)参数,用来判断这个参数是否为null,若这个参数为null,则程序正常运行,若不为null,则程序报错并打印如下日志:
上图的断言参数是一个赋值了"hello0"的String类型对象,如下代码:
@Test
public void test3() {
String str = "hello";
Assertions.assertNull(str);
}
那么不难理解,assertNotNull 就是表示当参数为空时,程序报错并打印日志。
3、用例的执行顺序
3.1、用例执行顺序是怎样的?
junit 5 官方网站并没有明确说明用例默认的执行顺序的规则,实际上,测试用例并不会按照我们编写的用例的顺序来执行,这会带来什么问题呢?
例如,我们要对QQ登录页面编写测试用例,那么我们期望的顺序是:1.检查登录页面是否正确、2.检查能否正常登录、3.异常登录。但由于实际的用例执行顺序不可知,有可能就变成了先检查“2.能否正常登录”,然后再“1.检查登录页面是否正常”,显然不合理,那么我们有什么办法控制测试用例的执行顺序呢?接着往下看~
3.2、通过order注解来排序
首先要先使用注解 @TestMethodOrder(MethodOrderer.OrderAnnotation.class) 说明当前类下素有的用例需要使用 order 注解来进行排序(注意:该注解必须要用在类上),然后通过Order注解来指定测试用例的执行顺序,该注解的参数便是执行顺序。
例如,现在有三个测试方法,按照指定顺序执行,如下代码:
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class Test2 {
@Test
@Order(1)
public void aaa() {
System.out.println("第一执行aaa");
}
@Test
@Order(2)
public void bbb() {
System.out.println("第二执行bbb");
}
@Test
@Order(3)
public void ccc() {
System.out.println("第三执行ccc");
}
}
执行结果如下:
4、参数化
什么是参数化的用例呢?就是尽可能的通过一个用例,多组参数来模拟用户的行为。在使用参数化的注解之前需要先用注解 @ParameterizedTest 声明该方法为参数化方法,接着参数化的注解有两种实现方法,如下:
Ps:使用了参数化注解的方法,就不用使用@Test注解了~
4.1、单参数
单参数便是指测试的方法只有一个参数~
通过 @ValueSource 注解提供数据来源。
使用方式: @ValueSource(数据类型方法={参数1,参数2....})
数据类型方法如下(来自官方文档):
例如,有三个String类型的参数,如下代码:
class Solution {
@ParameterizedTest
@ValueSource(strings = {"周杰伦", "林俊杰", "薛之谦"})
public void getString(String str) {
System.out.println(str);
}
}
执行结果如下:
4.2、多参数
多参数便是指测试的方法有多个参数~
4.2.1、方法一:注解里手动编写数据源
通过 @CsvSource 注解提供数据来源。
使用方式:每一个双引号就是一组参数(测试用例)
如下代码:
@ParameterizedTest
@CsvSource({"周杰伦,18", "林俊杰,19", "薛之谦,20"})
public void getString(String name, String age) {
System.out.println("name:" + name + ",age:" + age);
}
执行结果:
但是当我们的测试用例有几十上百个的时候,再用这个方法来写测试用例就不合适了,那么就可以使用以下办法~
4.2.2、方法二:从第三方csv文件读取数据源
针对一个测试方法有很多种用例时,我们可以使用Excel表格来填写数据,具体的,先创建一个csv为后缀的文件后,一定要注意,使用系统自带的Excel工具来打开和编辑csv文件,如下:
通过 @CsvFileSource 注解提供数据来源。
使用方式:注解的参数就是写测试用例的文件的绝对路径。
如下代码:
@ParameterizedTest
@CsvFileSource(files = "D:\\mytest\\test.csv")
public void getString(String name, String age) {
System.out.println("name:" + name + ",age:" + age);
}
执行结果:
4.3、动态参数
如果我们希望测试的参数是动态改变的(比如引入时间戳),就需要写一个方法,在这个方法里创建随机测试实例,然后将这个方法作为数据源,也就是 @MethodSource 注解的参数(表示参数的该方法必须是静态的方法)。
如下代码:
@ParameterizedTest
@MethodSource("createString")//匹配的方法必须是静态的
public void getString(String name, int id) {
System.out.println("name:" + name + ",id:" + id);
}
/**
* 注意:
* 1.首先方法必须是静态的
* 2.如果返回的数据类型是统一的,就直接写具体类型。
* 如果不统一,就需要使用组合类型Arguments
* @return
*/
public static Stream<Arguments> createString() {
String[] arr = new String[3];
for(int i = 0; i < 3; i++) {
arr[i] = System.currentTimeMillis() + "";
}
return Stream.of(
Arguments.arguments(arr[0], 1),
Arguments.arguments(arr[1], 2),
Arguments.arguments(arr[2], 3)
);
}
执行结果:
5、测试套件
有时候我们需要用一个或多了个类下的测试用例,或者是指定包来运行包下的所有测试用例,就需要用到测试套件。
5.1、指定类来运行用例
首先要创建一个类,通过 @Suite 注解标识该类为测试套件类(不是测试类),然后使用 @SelectClasses 注解来声明我们要指定的类(通过这个类来运行测试用例),如下代码:
Ps:要运行的用例必须要使用 @Test 注解
测试套件类如下:
@Suite
@SelectClasses({one.class, two.class})
public class RunSuite {
}
测试类如下:
public class one {
@Test
public void aaa() {
System.out.println("aaa");
}
@Test
public void bbb() {
System.out.println("bbb");
}
}
public class two {
@Test
public void ccc() {
System.out.println("ccc");
}
@Test
public void ddd() {
System.out.println("ddd");
}
}
执行结果如下:
5.2、指定包名来运行包下的所有用例
Ps:如果使用包名来指定运行的范围,那么该包下的所有的测试类的命名需要以Test或者Tests结尾(T必须大写);
代码如下:
@Suite
@SelectPackages("com.oneTest")
public class RunSuite {
}