您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   Code  
会员   
   
 
     
   
 
 订阅
自动化测试之JUnit单元测试框架
 
 
   次浏览      
 2023-11-28
 
编辑推荐:
本文主要介绍了单元测试框架 Junit,这里的单元测试指的是对最小的软件设计单元(模块)进行验证,在UI自动化测试里面,我们的单元测试主要针对UI界面的功能进行自动化测试。希望对你的学习有帮助。
本文来自于CSDN,由火龙果软件Linda编辑、推荐。

本节课我们来学习单元测试框架 Junit,这里的单元测试指的是对最小的软件设计单元(模块)进行验证,在UI自动化测试里面,我们的单元测试主要针对UI界面的功能进行自动化测试。

一、什么是 JUnit

JUnit是一个Java单元测试框架。它由Kent Beck和Erich Gamma建立,逐渐成为源于Kent Beck的sUnit的xUnit家族中最为成功的一个JUnit有它自己的JUnit扩展生态圈。多数Java的开发环境都已经集成了JUnit作为单元测试的工具。

注意:Junit 测试也是程序员测试,即所谓的白盒测试,它需要程序员知道被测试的代码如何完成功能,以及完成什么样的功能

它包括以下特性:

用于测试期望结果的断言(Assertion)

用于共享共同测试数据的测试工具

用于方便的组织和运行测试的测试套件

图形和文本的测试运行器

使用 Junit 能让我们快速的完成单元测试。

通常我们写完代码想要测试这段代码的正确性,那么必须新建一个类,然后创建一个 main() 方法,然后编写测试代码。如果需要测试的代码很多呢?那么要么就会建很多main() 方法来测试,要么将其全部写在一个 main() 方法里面。这也会大大的增加测试的复杂度,降低程序员的测试积极性。而 Junit 能很好的解决这个问题,简化单元测试,写一点测一点,在编写以后的代码中如果发现问题可以较快的追踪到问题的原因,减小回归错误的纠错难度。

二、JUnit5 相关技术

通常我们使用 JUnit5 版本

1.注解

注解(也可以称为 元数据)为在代码中添加信息提供了一种形式化的方法,使得在代码中任一时刻可以非常方便的使用这些数据;注解类型定义了一种新的特殊接口类型,在接口关键期 interface 之前加@符号,即用@interface即可区分注解与普通接口声明。目前大部分框架都是通过使用注解简化代码提高编码效率

1.1 @Test

JUnit提供了非常强大的注解功能,通过 @Test 注解修饰到方法上,该方法就变为了一个测试方法,执行当前类时,会自动的执行该类下所有带 @Test 注解的用例

使用这些JUnit提供的注解时,需要在pom.xml文件中进行配置(记得配置完要进行刷新):maven中央仓库

  1. <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
  2. <dependency>
  3. <groupId>org.junit.jupiter</groupId>
  4. <artifactId>junit-jupiter-api</artifactId>
  5. <version>5.9.2</version>
  6. <scope>test</scope>
  7. </dependency>

  1. <dependencies>
  2. <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
  3. <dependency>
  4. <groupId>org.junit.jupiter</groupId>
  5. <artifactId>junit-jupiter-api</artifactId>
  6. <version>5.9.2</version>
  7. </dependency>
  8. </dependencies>

 

在 main 文件中,我们需要导入 Test 包:

我们写一个测试用例:

  1. import org.junit.jupiter.api.Test;
  2. public class JUnitDemo1 {
  3. @Test
  4. void Test01() {
  5. System.out.println("这是 JunitDemo1 里的 Test01");
  6. }
  7. @Test
  8. void Test02() {
  9. System.out.println("这是 JunitDemo1 里的 Test02");
  10. }
  11. }

 

❗❗需要注意的是写的代码几个运行按钮:

其中“对号”表示测试通过;“ !”表示测试错误

1.2 @Disabled

Disabled 表示 忽略

  1. import org.junit.jupiter.api.Disabled;
  2. import org.junit.jupiter.api.Test;
  3. import org.openqa.selenium.By;
  4. import org.openqa.selenium.WebDriver;
  5. import org.openqa.selenium.chrome.ChromeDriver;
  6. public class JUnitDemo1 {
  7. @Test
  8. void Test01() {
  9. System.out.println("这是 JunitDemo1 里的 Test01");
  10. }
  11. @Test
  12. void Test02() {
  13. System.out.println("这是 JunitDemo1 里的 Test02");
  14. }
  15. @Disabled
  16. void Test03() {
  17. WebDriver webDriver = new ChromeDriver();
  18. webDriver.get("https://www.baidu.com");
  19. webDriver.findElement(By.cssSelector("#kw")).sendKeys("521");
  20. webDriver.findElement(By.cssSelector("#su")).click();
  21. }
  22. }

此时我们看到这个代码就不执行 Test03

1.3 @BeforeAll、@AfterAll

@BeforeAll:当前的方法需要在当前类下所有用例执行之前执行一次,且被该注解修饰的方法必须为静态方法

@AfterAll:当前的方法需要在当前类下所有用例执行之后执行一次,且被该注解修饰的方法必须为静态方法

例如在 UI 自动化中:通常情况下,创建文件、打开网页放在BeforeAll 里边;关闭浏览器放在AfterAll 里边

  1. import org.junit.jupiter.api.AfterAll;
  2. import org.junit.jupiter.api.BeforeAll;
  3. import org.junit.jupiter.api.Disabled;
  4. import org.junit.jupiter.api.Test;
  5. import org.openqa.selenium.By;
  6. import org.openqa.selenium.WebDriver;
  7. import org.openqa.selenium.chrome.ChromeDriver;
  8. public class JUnitDemo1 {
  9. @Test
  10. void Test01() {
  11. System.out.println("这是 JunitDemo1 里的 Test01");
  12. }
  13. @Test
  14. void Test02() {
  15. System.out.println("这是 JunitDemo1 里的 Test02");
  16. }
  17. @Disabled
  18. void Test03() {
  19. WebDriver webDriver = new ChromeDriver();
  20. webDriver.get("https://www.baidu.com");
  21. webDriver.findElement(By.cssSelector("#kw")).sendKeys("521");
  22. webDriver.findElement(By.cssSelector("#su")).click();
  23. }
  24. @BeforeAll
  25. static void SetUp() {
  26. System.out.println("这是我们 BeforeAll 里边的语句");
  27. }
  28. @AfterAll
  29. static void TearDown() {
  30. System.out.println("这是我们 AfterAll 里边的语句");
  31. }
  32. }

1.4 @BeforeEach、@AfterEach

@BeforeEach:当前的方法需要在每个用例执行之前都执行一次

@AfterEach:当前的方法需要在每个用例执行之后都执行一次

  1. import org.junit.jupiter.api.*;
  2. import org.openqa.selenium.By;
  3. import org.openqa.selenium.WebDriver;
  4. import org.openqa.selenium.chrome.ChromeDriver;
  5. public class JUnitDemo1 {
  6. @Test
  7. void Test01() {
  8. System.out.println("这是 JunitDemo1 里的 Test01");
  9. }
  10. @Test
  11. void Test02() {
  12. System.out.println("这是 JunitDemo1 里的 Test02");
  13. }
  14. @Disabled
  15. void Test03() {
  16. WebDriver webDriver = new ChromeDriver();
  17. webDriver.get("https://www.baidu.com");
  18. webDriver.findElement(By.cssSelector("#kw")).sendKeys("521");
  19. webDriver.findElement(By.cssSelector("#su")).click();
  20. }
  21. @BeforeAll
  22. static void SetUp() {
  23. System.out.println("这是我们 BeforeAll 里边的语句");
  24. }
  25. @AfterAll
  26. static void TearDown() {
  27. System.out.println("这是我们 AfterAll 里边的语句");
  28. }
  29. @BeforeEach
  30. void BeforeEachTest() {
  31. System.out.println("这是 BeforeEach 里边的语句");
  32. }
  33. @AfterEach
  34. void AfterEachTest() {
  35. System.out.println("这是 AfterEach 里边的语句");
  36. }
  37. }

2.参数化

参数化就是尽可能的通过一个用例,多组参数来模拟用户的行为;在使用参数化注解之前需要先用 @parameterizedTest 声明该方法为参数化方法,然后再通过注解提供数据来源

使用 @parameterizedTest 需要在 pom.xml 文件中进行配置(记得配置完要进行刷新):

  1. <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-params -->
  2. <dependency>
  3. <groupId>org.junit.jupiter</groupId>
  4. <artifactId>junit-jupiter-params</artifactId>
  5. <version>5.9.2</version>
  6. </dependency>

2.1 单参数

单参数:@ValueSource(数据类型方法={参数1,参数2…})

  1. import org.junit.jupiter.params.ParameterizedTest;
  2. import org.junit.jupiter.params.provider.ValueSource;
  3. public class JUnitDemo2 {
  4. //单参数:@ValueSource(数据类型方法={参数1,参数2…})
  5. @ParameterizedTest
  6. @ValueSource(ints = {1, 2, 3})
  7. void Test01(int num) {
  8. System.out.println(num);
  9. }
  10. @ParameterizedTest
  11. @ValueSource(strings = {"1", "2", "3"})
  12. void Test02(String number) {
  13. System.out.println(number);
  14. }
  15. }

2.2 CSV 获取参数

假设上述参数有很多,在注解处手动编写数据源就有些不方便,我们这时就可以借助第三方csv文件来读取数据源

CSV 获取参数:@CsvFileSource(resources = "____.csv")

这个时候我们需要在 main 文件下 resources 中创建一个 test.csv 文件(.csv 和 参数必须相同):

  1. 张三1,李四1,王五1
  2. 张三2,李四2,王五2

代码编写:

  1. import org.junit.jupiter.params.ParameterizedTest;
  2. import org.junit.jupiter.params.provider.CsvFileSource;
  3. //参数化
  4. public class JUnitDemo2 {
  5. @ParameterizedTest
  6. @CsvFileSource(resources = "test.csv")
  7. void Test03(String name) {
  8. System.out.println(name);
  9. }
  10. }

此时我们就把所有的内容都打印出来了;

2.3 方法获取参数

@CsvFileSource、@ValueSource 只能传递同种类型的参数,那么我们想要传多种参数,那么可以用方法获取参数

方法获取参数:@MethodSource("Generator")

  1. import org.junit.jupiter.params.ParameterizedTest;
  2. import org.junit.jupiter.params.provider.Arguments;
  3. import org.junit.jupiter.params.provider.MethodSource;
  4. import java.util.stream.Stream;
  5. public class JUnitDemo2 {
  6. public static Stream<Arguments> Generator() {
  7. return Stream.of(Arguments.arguments(1, "张三"),
  8. Arguments.arguments(2, "李四"),
  9. Arguments.arguments(3, "王五")
  10. );
  11. }
  12. @ParameterizedTest
  13. @MethodSource("Generator")
  14. void Test04(int num, String name) {
  15. System.out.println(num + ":" + name);
  16. }
  17. }

2.4 多参数

多参数:@CsvSource({“数据组合1”,“数据组合2”…}),每个双引号是一组参数(测试用例)

  1. import org.junit.jupiter.params.ParameterizedTest;
  2. import org.junit.jupiter.params.provider.*;
  3. import java.util.stream.Stream;
  4. public class JUnitDemo2 {
  5. //多参数:@CsvSource({“数据组合1”,“数据组合2”…})
  6. @ParameterizedTest
  7. @CsvSource({"1, 张三", "2, 李四", "3, 王五"})
  8. void manyTest(int num, String name) {
  9. System.out.println("num:" + num + ", name:" + name);
  10. }
  11. }

3.测试用例的执行顺序

我们看以下代码:

此时我们看到打印的依次是 A、B、C

此时我们看到打印的还是 A、B、C

所以 测试用例的执行顺序并不会按照我们编写代码的顺序来执行

3.1 顺序执行:@TestMethodOrder(MethodOrderer.OrderAnnotation.class)

如果在实际测试中,我们需要完成连贯的多个步骤的测试,是需要规定测试用例执行的顺序的,可以通过 @order 注解来实现排序

先使用注解说明当前类下所有的用例需要使用 @order 注解来进行排序(注意:该注解必须要用在类上)

然后通过 @order 来指定用例的具体顺序

顺序执行:@TestMethodOrder(MethodOrderer.OrderAnnotation.class),然后再每次执行的时候添加 @order(执行顺序)

  1. import org.junit.jupiter.api.MethodOrderer;
  2. import org.junit.jupiter.api.Order;
  3. import org.junit.jupiter.api.Test;
  4. import org.junit.jupiter.api.TestMethodOrder;
  5. @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
  6. public class JUnitDemo1 {
  7. @Order(2)
  8. @Test
  9. void test03() {
  10. System.out.println("C");
  11. }
  12. @Order(3)
  13. @Test
  14. void test01() {
  15. System.out.println("A");
  16. }
  17. @Order(1)
  18. @Test
  19. void test02() {
  20. System.out.println("B");
  21. }
  22. }

 

这个时候我们通过 @Order 来设置执行顺序,按照这个思路,我们得到的结果为 B -> C -> A

3.2 随机执行:@TestMethodOrder(MethodOrderer.class)

随机执行:@TestMethodOrder(MethodOrderer.class),也就是我们看到的一开始的执行顺序

  1. import org.junit.jupiter.api.MethodOrderer;
  2. import org.junit.jupiter.api.Test;
  3. import org.junit.jupiter.api.TestMethodOrder;
  4. @TestMethodOrder(MethodOrderer.class)
  5. public class JUnitDemo2 {
  6. @Test
  7. void test03() {
  8. System.out.println("C");
  9. }
  10. @Test
  11. void test01() {
  12. System.out.println("A");
  13. }
  14. @Test
  15. void test02() {
  16. System.out.println("B");
  17. }
  18. }

因此,如果不写 @TestMethodOrder(MethodOrderer.OrderAnnotation.class) 则是随即执行

4.断言

检查测试方法的期望结果值和真实返回值,通过 Assertions 类实现

断言匹配/不匹配:assertEquals()、assertNotEquals()

断言结果为真/为假:assertTrue()、assertFalse()

断言结果为空/非空:assertNull()、assertNotNull()

  1. public class JUnitDemo3 {
  2. @Test
  3. void test01() {
  4. Assertions.assertTrue(1 == 1);
  5. }
  6. @Test
  7. void test02() {
  8. Assertions.assertTrue(2 == 1);
  9. }
  10. }

在这里我们可以看到 预期和结果 是否相同

5.测试套件

当我们一个类中有多个测试用例时,我们不可能挨个去运行,那样将会很耗费时间,这时我们就需要 测试套件 来指定类或者指定包名来运行类下或者包下的所有测试用例。

如果要使用测试套件,首先我们需要先创建一个类,通过 @Suite 注解标识该类为测试套件类(而不是测试类)

使用测试套件,需要引入依赖:

  1. <!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-suite -->
  2. <dependency>
  3. <groupId>org.junit.platform</groupId>
  4. <artifactId>junit-platform-suite</artifactId>
  5. <version>1.9.1</version>
  6. </dependency>

还需要引入依赖(使用 suite 需要引入 引擎engine 依赖):

  1. <!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-suite -->
  2. <dependency>
  3. <groupId>org.junit.platform</groupId>
  4. <artifactId>junit-platform-suite</artifactId>
  5. <version>1.9.1</version>
  6. </dependency>

 

5.1 通过 class 运行测试用例

使用 @SelectClasses({指定类, 指定类, 指定类})

JUnitDemo1:

  1. @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
  2. public class JUnitDemo1 {
  3. @Order(2)
  4. @Test
  5. void test03() {
  6. System.out.println("C");
  7. }
  8. @Order(3)
  9. @Test
  10. void test01() {
  11. System.out.println("A");
  12. }
  13. @Order(1)
  14. @Test
  15. void test02() {
  16. System.out.println("B");
  17. }
  18. }

 

JUnitDemo4:

  1. public class JUnitDemo4 {
  2. public static Stream<Arguments> Generator() {
  3. return Stream.of(Arguments.arguments(1, "张三"),
  4. Arguments.arguments(2, "李四"),
  5. Arguments.arguments(3, "王五")
  6. );
  7. }
  8. @ParameterizedTest
  9. @MethodSource("Generator")
  10. void Test04(int num, String name) {
  11. System.out.println(num + ":" + name);
  12. }
  13. }

 

RunSuite:

  1. @Suite
  2. @SelectClasses({JUnitDemo4.class, JUnitDemo1.class})
  3. public class RunSuite {
  4. }

此时就是先执行 JUnitDemo4,再执行 JUnitDemo1

5.2 通过包运行测试用例

@SelectPackages(value = {"包1", "包2","..."})

JUnit1 包下 Test1:

  1. public class Test1 {
  2. @Test
  3. void test1() {
  4. System.out.println("JUnit1 package Test1 test1");
  5. }
  6. }

 

JUnit2 包下 Test2:

  1. public class Test2 {
  2. @Test
  3. void test2() {
  4. System.out.println("JUnit2 package Test2 test2");
  5. }
  6. }

 

RunSuite2:

  1. @Suite
  2. @SelectPackages(value = {"JUnit1", "JUnit2"})
  3. public class RunSuite2 {
  4. }

 

此时先执行 JUnit1 包下 Test1 ,再执行 JUnit2 包下 Test2

 

   
次浏览       
相关文章

微服务测试之单元测试
一篇图文带你了解白盒测试用例设计方法
全面的质量保障体系之回归测试策略
人工智能自动化测试探索
相关文档

自动化接口测试实践之路
jenkins持续集成测试
性能测试诊断分析与优化
性能测试实例
相关课程

持续集成测试最佳实践
自动化测试体系建设与最佳实践
测试架构的构建与应用实践
DevOps时代的测试技术与最佳实践

最新活动计划
C++高级编程 12-25 [线上]
白盒测试技术与工具实践 12-24[线上]
LLM大模型应用与项目构建 12-26[特惠]
需求分析最佳实践与沙盘演练 1-6[线上]
SysML建模专家 1-16[北京]
UAF架构体系与实践 1-22[北京]
 
 
最新文章
大数据平台测试
微服务架构下的测试之道
从零开始掌握微服务软件测试
如何进行测试需求分析:从接收需求到用例设计
python_selenium自动化测试框架
最新课程
测试需求分析与测试用例设计
性能测试方法与技术
自动化测试框架设计高级实践
接口自动化测试方法与工具
软件测试方法与实践(贯穿案例)
更多...   
成功案例
某支付企业 单元测试与重构培训
北京 用户体验、可用性测试与评估
某军工研究单位 自动化测试方法、案例与工具
知名消费金融公司 探索性测试与测试分析
北京 航天科工某子公司 软件测试架构师
更多...