当前位置: 首页 > article >正文

Junit 5 单元测试框架

目录

1. Junit 5 的依赖

2. Junit - 注解

2.1 @Test

2.2 @BeforeEach

2.3 @BeforeAll

2.4 @AfterEach

2.5 @AfterAll

3. Junit - 断言

3.1 assertEquals/ assertNotEquals 断言是否匹配

3.2 assertTrue / assertFalse 断言结果的真假

3.3 assertNull / assertNotNull 断言结果是否为空

4. Junit - 测试用例的执行顺序

5. Junit - 参数化

5.1 单参数数据来源

5.2 多参数数据来源 - 在注解里编写数据源

5.3 多参数数据来源 - 从第三方 csv 文件读取数据源

5.4 多参数数据来源 - 通过动态方法来提供数据源

6. JUnit - 测试套件

6.1 指定类来运行用例

6.2 指定包名来运行包下所有的用例


1. Junit 5 的依赖

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.8.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-suite</artifactId>
    <version>1.8.2</version>
    <scope>test</scope>
</dependency>

如果之前已经下载过 Junit 4 的相关依赖, 那么则需要先删除本地仓库的依赖, 再下载 Jnit5 的依赖.

2. Junit - 注解

2.1 @Test

该注解是用来加在测试的方法上面的, 表示该方法是测试方法, 并且当我们给方法加上  @Test 注解的时候, 当前方法就可以自己运行了, 不再需要 main 函数来调用. 而且当前类也可以自己运行了 , 运行当前类表示执行当前类里面所有加了 @Test 注解的方法.

不使用 @Test 注解之前, 测试方法需要的步骤 : 

第一步 : 编写测试类, 编写测试方法.

第二步 : 编写运行测试类, 添加 main 方法, 实例化测试类, 调用相关测试方法.

 

使用 @Test 注解之后, 测试方法需要的步骤 : 

由此对比, 使用 Junit 提供的注解来辅助自动化测试, 大大简化了代码的编写, 更加的方便了测试人员更好的进行测试.

并且, 在一个测试类中, 你想让哪些方法执行, 就在该方法上加 @Test 注解即可, 不想让它执行, 就去掉 @Test 注解即可, 非常方便.

 执行结果 : 

2.2 @BeforeEach

该注解是修饰方法的, 当一个测试方法加上 @BeforeEach 注解之后, 就表示当前方法需要在每个测试用例执行之前都执行一次. (注意加上 @BeforeEach 注解之后, 就不要加 @Test 注解了)

代码示例 : 

public class JunitTest {
    @BeforeEach
    void baseController() {
        System.out.println("before each");
    }
    @Test
    void base1() {
        System.out.println("bbb");
    }
    @Test
    void base2() {
        System.out.println("ccc");
    }
}

执行结果 : 

2.3 @BeforeAll

该注解是修饰方法的, 当一个测试方法加上 @BeforeAll 注解之后, 就表示当前方法需要在当前类下所有测试用例执行之前执行一次. 并且被该注解修饰的方法必须为静态方法. 

在测试项目的时候, 可以把创建驱动对象单独写一个方法, 使用 @BeforeAll 注解修饰. 

代码示例 : 

public class JunitTest {
    @BeforeAll
    static void baseController() {
        System.out.println("before all");
    }
    @Test
    void base1() {
        System.out.println("bbb");
    }
    @Test
    void base2() {
        System.out.println("ccc");
    }
}

执行结果 :

2.4 @AfterEach

该注解是修饰方法的, 当一个测试方法加上 @AfterEach 注解之后, 就表示当前方法需要在每个测试用例执行之后都执行一次.

代码示例 : 

public class JunitTest {
    @AfterEach
    void baseController() {
        System.out.println("after each");
    }
    @Test
    void base1() {
        System.out.println("bbb");
    }
    @Test
    void base2() {
        System.out.println("ccc");
    }
}

执行结果 :  

2.5 @AfterAll

该注解是修饰方法的, 当一个测试方法加上 @AfterAll 注解之后, 就表示当前方法需要在当前类下所有测试用例执行之后执行一次. 并且被该注解修饰的方法必须为静态方法.

在测试项目的时候, 可以把释放驱动对象单独写一个方法, 使用 @AfterAll 注解修饰. 

代码示例 : 

public class JunitTest {
    @AfterAll
    static void baseController() {
        System.out.println("after all");
    }
    @Test
    void base1() {
        System.out.println("bbb");
    }
    @Test
    void base2() {
        System.out.println("ccc");
    }
}

 执行结果 :

3. Junit - 断言

3.1 assertEquals/ assertNotEquals 断言是否匹配

例如我们获取一下百度首页中百度一下按钮上的文字 - 百度一下, 假如我们的预期结果是百度两下, 而打印的是百度一下, 此时就可以使用 assertEquals 来断言是否匹配. 该方法有两个参数, 第一个参数是预期结果, 第二个参数是实际结果.

代码示例 : 

@Test
void baseAssert() {
    EdgeDriver driver = new EdgeDriver();
    driver.get("https://www.baidu.com");
    // 获取百度一下按钮上属性对应的值 - 百度一下
    String text = driver.findElement(By.cssSelector("#su"))
                                    .getAttribute("value");
    System.out.println("预期结果: 百度两下, 实际结果 : " + text);
    // 断言匹配
    Assertions.assertEquals("百度两下", text); // false
    // 断言不匹配
    // Assertions.assertNotEquals("百度两下", text); // true
    driver.quit();
}

执行结果 : 

 使用 assertNotEquals 断言不匹配, 程序执行结果 : 

3.2 assertTrue / assertFalse 断言结果的真假

代码示例 : 

@Test
void baseAssert1() {
    Assertions.assertTrue(1 == 1); // true
}
@Test
void bassAssert2() {
    Assertions.assertTrue(1 == 0); // false
}
@Test
void bassAssert3() {
    Assertions.assertFalse(1 == 1); // false
}
@Test
void bassAssert4() {
    Assertions.assertFalse(1 == 0); // true
}

执行结果 : 

3.3 assertNull / assertNotNull 断言结果是否为空

代码示例 : 

@Test
void baseAssert5() {
    String res = null;
    Assertions.assertNull(res);
}
@Test
void baseAssert6() {
    String res = "hello";
    Assertions.assertNull(res);
}

执行结果 :  

4. Junit - 测试用例的执行顺序

先来看默认情况下, 多个测试用例的执行顺序.

代码示例 :

public class JunitTest {
    @Test // 2
    void editLoginTest() {
        System.out.println("loginTest");
    }
    @Test // 1
    void indexTest() {
        System.out.println("indexTest");
    }
    @Test // 3
    void editTest() {
        System.out.println("editTest");
    }
}

执行结果 : 

从执行结果来看, 默认情况下, 多个测试用例的执行顺序是随机的 (可以调整顺序, 多运行几次) . 那么有时候我们一些登录的场景, 是需要按照顺序进行测试的, 比如说博客的登录系统, 我们要登录之后, 才能进入到博客列表页, 博客编辑页, 此时就需要针对多个测试用例指定执行顺序了.

使用 @Order 注解来指定多个测试用例的执行顺序 : 

1. 先要使用 @TestMethodOrder 注解来说明当前类下所有的用例需要使用 Order 注解来进行排序, 当然也可以根据方法名排序, 但是常用的还是 Order 指定方法排序.

2. 然后通过 @Order 注解来指定测试用例的具体顺序.

3. @Test 注解不能删除.

代码示例 : 

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class JunitTest {
    @Test
    @Order(1)
    void editLoginTest() {
        System.out.println("loginTest");
    }
    @Test
    @Order(2)
    void indexTest() {
        System.out.println("indexTest");
    }
    @Test
    @Order(3)
    void editTest() {
        System.out.println("editTest");
    }
}

执行结果 : 

5. Junit - 参数化

在登录场景中, 如果我们不使用参数化, 那么多个用户登录的时候, 我们的测试用例写起来就会非常麻烦, 如下 : 

 而我们期望的是尽可能的通过一个测试用例, 多组参数来模拟用户的登录行为.

1. 在使用参数化注解之前需要先使用 @ParamterizedTest 声明该方法为参数化方法.

2. 通过注解提供数据来源.

5.1 单参数数据来源

使用 @ValueSource 注解

@ValueSource(数据类型方法 = {参数1, 参数2, 参数3 .....})

@ParameterizedTest
@ValueSource(strings = {"张三", "李四", "王五"})
void singleParamTest(String name) {
    System.out.println(name);
}

执行结果 : 

5.2 多参数数据来源 - 在注解里编写数据源

使用 @CsvSource 注解.

@CsvSource({"第一组数据", "第二组数据", "第三组数据", ....}) , 每一个双引号, 就是一组测试用例.

@ParameterizedTest
@CsvSource({"张三, 123","李四, 123", "王五, 123"})
void muchParamTest(String name, int age) {
    System.out.println("name: " + name + ", age : " + age);
}

执行结果 : 

5.3 多参数数据来源 - 从第三方 csv 文件读取数据源

多参数场景下, 当在注解中的测试用例越来越多时, 代码的可读性就会非常差, 此时可以使用 @CsvFileSource 注解来从第三方 csv 文件中读取数据源.

@CsvFileSource(files = "文件路径")

@ParameterizedTest
@CsvFileSource(files = "D:\\aa\\abc.csv")
void csvFileParamTest(String name, int age) {
    System.out.println("name: " + name + ", age : " + age);
}

执行结果 : 

5.4 多参数数据来源 - 通过动态方法来提供数据源

使用 @MethodSource 注解

@ParameterizedTest
@MethodSource("methodParams")
void dynamicMethodParamTest(String name, int age) {
    System.out.println("name : " + name + ",age : " + age);
}
static Stream<Arguments> methodParams() throws InterruptedException {
    // 构造动态参数
    String[] array = new String[5];
    for(int i = 0; i < array.length; ++i) {
        Thread.sleep(500);
        array[i] = String.valueOf(System.currentTimeMillis());
    }
    return Stream.of(
            Arguments.arguments(array[0], 21),
            Arguments.arguments(array[1], 22),
            Arguments.arguments(array[2], 23),
            Arguments.arguments(array[3], 24),
            Arguments.arguments(array[4], 25)
    );
}

执行结果 : 

此处指定的方法的返回类型 Arguments, 是因为一组数据的数据类型不同, 由 String 和 int 类型组合来的, 因此要使用组合类型  Arguments. 如果相同, 就可以写具体的类型.

【其他注意事项]

1. 当我们不指定数据源方法名时, 会自动的找跟用例方法名同名的静态方法.

2. 使用了参数化注解的方法不能再用 @Test 注解. (@Test 只能作用在非参数化的用例上)

6. JUnit - 测试套件

测试套件是用来指定当前包下哪些类中测试方法需要被执行, 如果没有测试套件, 我们很难去同时执行几个类下的测试用例. 

准备工作 : 在 autoTest5 包下创建 aaa, bbb, ccc, 三个测试类, 分别给每个类加上测试方法,  以及前面创建好的 JunitTest 测试类. 

6.1 指定类来运行用例

创建 runSuite 测试套件类, 添加@Suite注解, @SelectClasses 注解, 指定类运行类下的加了 @Test 注解, 或者参数化的测试用例.

@Suite
@SelectClasses({aaa.class,ccc.class})
public class runSuite {
}

运行测试套件, 执行结果 : 

6.2 指定包名来运行包下所有的用例

此时 autoTest5 包下有如下几个类 : 

 改写测试套件代码 : 

@Suite
@SelectPackages("com.autoTest5")
public class runSuite {
}

执行结果 : 

 autoTest5 包下明明有 4 个类, 为啥只运行了一个类中的测试用例呢 ? 

如果使用包名来指定运行的范围, 那么该包下所有测试类的命名必须要以 Test/Tests 结尾 (T 必须要大写)

改类名 : 

 执行结果 : 


本篇文章就到这里了, 谢谢观看~


http://www.kler.cn/a/7355.html

相关文章:

  • CPU执行指令的过程
  • MySQL数据库:SQL语言入门 【3】(学习笔记)
  • 【CANOE】【学习】【DecodeString】字节转为中文字符输出
  • 概率论公式整理
  • cmake生成器表达式
  • 已解决:spark代码中sqlContext.createDataframe空指针异常
  • Matlab 一种计算植物面积密度的新方法(论文复现:凸包法)
  • 【C++】用一棵红黑树同时封装出map和set
  • 2022年业绩逆势增长,“要强”蒙牛再创蒙牛
  • Flutter 本地SQLite数据库版本升级处理
  • 数据分析之Pandas(2)
  • 【Go基础】一篇文章带你了解 — map
  • Lock wait timeout exceeded; try restarting transaction
  • 现代前端开发者的自我迷失,你还会前端基础知识吗?
  • 面向对象编程(基础)10:类的成员之三:构造器(Constructor)
  • Android Binder小结
  • 【蓝桥杯算法模板题--蓝桥题库Java】
  • 技术宅小伙:ChatGPT的编程能力到底有多厉害?
  • React 面向组件编程(下)
  • leetcode剑指 Offer 11. 旋转数组的最小数字
  • 【PR】字幕处理
  • 双指针巧解链表有环问题
  • 算法设计-hw2
  • 晶振02——晶振不能放置在PCB边缘
  • windows搭建ftp及原理(小白向)
  • 国内怎么玩chatGPT中文版-国内怎么玩chatGPT4