目录
引言
注解
断言
用例的执行顺序
参数化
测试套件
引言
- Junit 是一个基于Java语言的单元测试框架
- Selenium 是一个用于Web应用程序测试的自动化测试框架
- 结合二者能让我们的 Web自动化测试 更加完善和全面
注意:
本文所讲的内容,均基于以下依赖,我们需自己手动引入至 pom.xml 中
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java --> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>3.141.59</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-io/commons-io --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.9.2</version> </dependency> <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-params --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-params</artifactId> <version>5.9.2</version> </dependency> <!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-suite --> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-suite</artifactId> <version>1.9.1</version> </dependency> <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.9.2</version> <scope>test</scope> </dependency>
注解
@Test
public class JunitTest { @Test void Test01() { System.out.println("这是 JuniteTest 里面的 Test01"); } }
- 将 @Test 注解放置在一个无参方法上,表示该方法是一个测试方法
- 执行 JunitTest 类时,会自动执行该类中所有带有 @Test 注解的测试方法
@BeforeEach
public class JunitTest { @BeforeEach void SetUp() { System.out.println("这是我们BeforeEach里面的语句"); } @Test void Test01() { System.out.println("这是 JuniteTest 里面的 Test01"); } @Test void Test02() { System.out.println("这是 JuniteTest 里面的 Test02"); } }
该段代码执行结果:
- @BeforeEach注解被用于标记一个方法,该方法会在每个测试方法执行之前被自动调用
@BeforeAll
public class JunitTest { @BeforeAll static void SetUp() { System.out.println("这是我们BeforeAll里面的语句"); } @Test void Test01() { System.out.println("这是 JuniteTest 里面的 Test01"); } @Test void Test02() { System.out.println("这是 JuniteTest 里面的 Test02"); } }
该段代码执行结果如下:
- @BeforeAll 注解被用于标记一个方法,该方法会在所有测试方法执行之前被自动调用
- @BeforeAll 注解修饰的方法必须是静态方法
@AfterEach
public class JunitTest { @AfterEach void TearDown() { System.out.println("这是AfterEach的语句"); } @Test void Test01() { System.out.println("这是 JuniteTest 里面的 Test01"); } @Test void Test02() { System.out.println("这是 JuniteTest 里面的 Test02"); } }
该段代码执行结果:
- @AfterEach注解被用于标记一个方法,该方法会在每个测试方法执行之后被自动调用
@AfterAll
public class JunitTest { @AfterAll static void TearDown() { System.out.println("这是AfterAll的语句"); } @Test void Test01() { System.out.println("这是 JuniteTest 里面的 Test01"); } @Test void Test02() { System.out.println("这是 JuniteTest 里面的 Test02"); }
该段代码执行结果:
- @AfterAll 注解被用于标记一个方法,该方法会在所有测试方法执行之后被自动调用
- @AfterAll 注解修饰的方法必须是静态方法
断言
断言是否匹配
public class JunitTest { @Test void Test09(){ int n = 1; // 断言是否相互匹配 Assertions.assertEquals(2,num); // 断言是否相互不匹配 Assertions.assertNotEquals(2,num); } }
该段代码运行结果:
- assertEquals() 方法的第一个参数为 期望结果,第二个参数为 实际结果
- 当 assertEquals() 方法的期望结果和实际结果不一致时,便会打印上图报错日志
- 当 assertEquals() 方法的期望结果和实际结果一致时,便什么都不会发生,继续执行其后面的代码
- 不难理解 assertNotEquals() 方法 跟 assertEquals() 方法 的逻辑正好相反
断言是否为真
public class JunitTest { @Test void Test09(){ boolean b = false; // 断言是否为真 Assertions.assertTrue(b); // 断言是否为假 Assertions.assertFalse(b); } }
该段代码运行结果:
- assertTrue() 方法用来判断所给状态是否为真
- 当 assertTrue() 方法中的状态不为真,便会打印上图报错日志
- 当 assertTrue() 方法中的状态为真,便什么都不会发生,继续执行其后面的代码
- 不难理解 assertFalse() 方法 跟 assertTrue() 方法 的逻辑正好相反
断言是否为空
public class JunitTest { @Test void Test09(){ // 断言是否为空 String str = null; String str2 = "1"; Assertions.assertNull(str2); //当传入 str 时 该断言不会报错 // 断言是否不为空 Assertions.assertNotNull(str2); } }
该段代码运行结果:
- assertNull() 方法用来判断所给参数是否为 null
- 当 assertNull() 方法中的参数不为 null,便会打印上图报错日志
- 当 assertNull() 方法中的参数为 null,便什么都不会发生,继续执行其后面的代码
- 不难理解 assertNotNull() 方法 跟 assertNull() 方法 的逻辑正好相反
用例的执行顺序
一个类中默认的用例执行顺序是什么?
- 在 Junit 框架中,默认的测试用例执行顺序是不确定的,也就是并不会按照我们所编写的用例的顺序来执行!
测试用例自定义顺序
//随机执行测试方法 //@TestMethodOrder(MethodOrderer.Random.class) //按自定义顺序执行测试方法 @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class JunitTest { @Order(1) @Test void A() { System.out.println("这是 Junite 里面的 A 测试用例"); } @Order(3) @Test void B() { System.out.println("这是 Junite 里面的 B 测试用例"); } @Order(2) @Test void Test01() { System.out.println("这是 JuniteTest 里面的 Test01"); } @Order(4) @Test void Test02() { System.out.println("这是 JuniteTest 里面的 Test02"); } }
该段代码执行结果:
- @TestMethodOrder(MethodOrderer.OrderAnnotation.class) 注解 写在相应类的上方
- 使用 @Order 注解并写在相应测试方法的上方,填写参数进行执行顺序的排序
- @TestMethodOrder(MethodOrderer.Random.class) 注解 表示该类随机执行测试方法,不需要搭配 @Order 注解,且每次执行该类时,测试方法的执行顺序都不相同
参数化
- 在写含有参数的测试方法时, @ParameterizedTest 注解 是必不可少的,它必须写在含参测试方法的上面,进行相应申明
- 使用 @ParameterizedTest 注解 时,无需再加 @Test 注解
单参数
- 单参数指的是测试方法有且仅有一个参数
public class JunitTest { @ParameterizedTest @ValueSource(ints = {1,2,3}) void Test04(int num){ System.out.println(num); } @ParameterizedTest @ValueSource(strings = {"小","林"}) void Test05(String str) { System.out.println(str); } }
该段代码执行结果:
- 该段代码 通过 @ValueSource 注释 来提供数据的来源
public class JunitTest { @ParameterizedTest @CsvFileSource(resources = "test01.csv") void Test06(String name) { System.out.println(name); } }
该段代码执行结果:
- 该段代码通过 @CsvFileSource 注释 来提供数据的来源 (适用于有大量测试用例的情况)
使用 @CsvFileSource 注释 的 操作步骤:
1. 在 resources 文件夹下创建后缀名为 .csv 的文件
2.在 @CsvFileSource 注解中,写上新创建的文件名字
3.点开新创建的文件,进行数据编写,每行代表 一个 name 参数,传入测试方法中
多参数
public class JunitTest { @ParameterizedTest @CsvSource({"1, 2, ''"}) // z 为一个空字符串且能被打印 void Test08(String x,String y,String z) { System.out.println(x); System.out.println(y); System.out.println(z); System.out.println("======================"); } @ParameterizedTest @CsvSource({"小林,18", "小王,20"}) void Test10(String name, String age) { System.out.println(name +" " + age); } }
该段代码执行结果:
- 该段代码 通过 @CsvSource 注释 来提供数据的来源
- @CsvSource 注释中的 每一个双引号就是一组参数
动态参数
public class JunitTest { @ParameterizedTest @MethodSource("Generator")//相匹配的方法必须为静态方法 void Test07(String str, int num) { System.out.println(str + ":" + num); } /* 注意: 1.首先方法必须为 静态方法 2.如果返回的数据类型时同一的,直接写该具体类型 如果不统一,需要使用组合类型 Arguments */ public static Stream<Arguments> Generator() throws InterruptedException { String[] arr = new String[3]; for (int i = 0; i < 3; i++) { // 这里加上 强制等待 是因为 该循环执行速度太快 时间戳 变化不明显 sleep(500); arr[i] = System.currentTimeMillis() + ""; } return Stream.of(Arguments.arguments(arr[0],1), Arguments.arguments(arr[1],2), Arguments.arguments(arr[2],3)); } }
该代码执行结果:
- 该段代码 通过 @MethodSource 注释 来提供数据的来源
- 作为动态参数我们可以写一个包含时间戳的静态方法来创建变化的数据
- 该静态方法作为数据源的同时,也是 @MethodSource 注释 的参数
测试套件
指定类名来运行类下的所有用例
JuniteTest 类:
public class JunitTest { @Test void Test01() { System.out.println("这是 JuniteTest 里面的 Test01"); } }
JuniteTest2 类:
public class JunitTest2 { @Test void Test01() { System.out.println("这是 JunitTest2 里面的 Test01"); } }
RunSuite 类:
@Suite //这里的测试用例类 按照我们所写的 class 的一个顺序来进行执行的 @SelectClasses({JunitTest.class,JunitTest2.class}) public class RunSuite { }
该段代码执行结果:
- 新创建一个 RunSuite 类
- 通过 @Suite 注解 声明该类为测试套件类(不是测试类)
- 我们便可通过 @SelectClasses 注解来自定义 测试类的执行顺序
- 所运行的测试方法 必须使用 @Test 注解
指定包名来运行包下的所有用例
package01 包:
package package01; public class Test01 { @Test void test01() { System.out.println("这是 包:package01 类:Test01 中的 test01 方法"); } }
package02 包:
package package02; import org.junit.jupiter.api.Test; public class Test02 { @Test void test02(){ System.out.println("这是 包:package02 类:Test02 中的 test02 方法"); } }
RunSuite 类:
@Suite //这里的测试用例类 按照我们所写的 package 的一个顺序来进行执行的 @SelectPackages(value = {"package02", "package01"}) public class RunSuite { }
该段代码执行结果:
- 新创建一个 RunSuite 类
- 通过 @Suite 注解 声明该类为测试套件类(不是测试类)
- 我们便可通过 @SelectPackages 注解来自定义 测试类的执行顺序