0
点赞
收藏
分享

微信扫一扫

Junit5 单元测试框架的使用

律楷粑粑 2022-01-24 阅读 102
单元测试

Junit5 简单使用总结

作为一款测试框架,一般我们需要从以下几个方面去考虑

  • TestCase : 测试用例的管理
  • Assertions : 用例断言的管理
  • Test Execution: 测试执行,以何种顺序执行
  • Test Fixtures : 测试装置,测试用例运行的前后动作,用来管理测试用例的执行
  • Test Suites: 测试套,控制用例批量运行
  • Test Runner: 测试用例的运行器
  • Test Result Report: 测试报告

Junit5 架构

junit5 大体由3个模块组成

  • Junit platform 其主要作用在JVM 上启动测试框架,支持通过命令行Gradle maven 和Api 来运行平台
  • Junit Jupiter :核心功能 用于编写测试用例
  • Junit vintage : 承载兼容老版本的用例运行

工程环境搭建 参考官网在pom.xml 文件中引入依赖

 <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>

<dependencies>
<!-- Only needed to run tests in a version of IntelliJ IDEA that bundles older versions -->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.8.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<!-- Maven 运行的依赖插件 -->
<plugins>

<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>

</plugins>
</build>

用例写作之常用注解

@Test标记在方法上,表示测试用例
@BeforeEach表示被注解的方法,会在每个测试用例执行之前运行,@BeforeEach 注解支持继承
@AfterEach表示被注解的方法,会在每个测试用例执行之后运行,@AfterEach 注解支持继承
@BeforeAll表示在所有的测试方法运行之前执行, @BeforeAll 注解可以被继承
@AfterAll表示所有的测试方法运行之后执行 @BeforeAll 注解可以被继承
@DisplayName声明测试类或者测试方法显示的名称,此注解不会被继承
@Tag用于类或者方法上,做标签,后续可以根据标签来选择用例的执行,此注解用于类上可以被继承,用于方法上不可以被继承
@Disabled用于类或者方法上 ,禁用测试类
@Nested嵌套类的使用,但必须要求内部类是非静态内部类
@ParameterizedTest参数化
@RepeatedTest用例重复执行,可以用来测试并发场景
@order指定用例的执行顺序,传入数字即可
@TestMethodOrder指定用例以何种顺序执行,传入指定的MethodOrder 就可以自定义实现顺序https://junit.org/junit5/docs/current/user-guide/#writing-tests-test-execution-order-methods
@DisplayName("测试计算器")
@TestMethodOrder(MethodOrderer.MethodName.class)
public class CalculatorTest {


@BeforeAll
public static void beforeAll(){
System.out.println("Calculator test Before All");
}


@BeforeEach
public void beforeEach(){
System.out.println("Calculator test Before Each");
}


@Test
@DisplayName("测试加法正常运算")
public void testAdd(){
int result = Calculator.add(2,4);
System.out.println(result);
Assertions.assertEquals(result,6);
}

@Test
@DisplayName("测试减法运算")
@Disabled
public void testSubtraction(){
int result = Calculator.subtraction(10,6);
System.out.println("减法运算结果是:" + result);
Assertions.assertEquals(result,4);
}

@Test
@DisplayName("测试乘法运算")
@Order(2)
public void testMultiplication(){
int result = Calculator.multiplication(2,4);

System.out.println("乘法运算结果是:" + result);
Assertions.assertEquals(result,8);
}

@Test
@DisplayName("测试除法运算")
@Order(3)
public void testDivision(){
int result = Calculator.division(10,3);
System.out.println("除法运算结果是:" + result);
Assertions.assertEquals(result,3);
}

@AfterEach
public void afterEach(){
System.out.println("Calculator test After each");
}

@AfterAll
public static void afterAll(){
System.out.println("Calculator test After All");
}
}

@Nested 嵌套测试

public class NestedDemoTest {


@Test
@DisplayName("外层测试")
public void testOuter(){
System.out.println("outer....");
}

@DisplayName("内层测试")
@Nested
class Inner01{
@Test
@DisplayName("inner01 test")
public void test(){
System.out.println("inner01");
}

@Nested
@DisplayName("inner01 嵌套")
class InInner{
@Test
public void test(){
System.out.println("inner01 嵌套inner 01");
}
}
}

@Nested
@DisplayName("inner02")
class Inner02{
@Test
@DisplayName("inner02 test")
public void test(){
System.out.println("inner02");
}
}
}

@Nested 用例,然后再执行第二层嵌套的用例: 外层->倒叙嵌套->第二层嵌套

junit5 断言体系

  • Assertions.assertEquals() 判断两个值是否相等
  • Assertions.assertNotEquals() 判断两个值是否不相等
  • Assertions.assertNull() 判断是否为空
  • Assertions.asserNotNull() 判断是否不为空
  • Assertions.assertSame() 判断两个对象的内存地址引用值是否相同
  • Assertions.assertNotSame() 判断两个对象的内存地址引用值是否不相等
  • assertTrue(Condition condition) 断言值为true
  • asserFalse(Condition condition) 断言值为false

Junit5 软断言的支持,软断言是指第一步校验不过,还可以继续向下校验

Assertions.assertAll(Executable… excutable) Executable 是一个函数式接口,可以通过lambda 表达式进行传值, …代表可变参数列表

 Assertions.assertAll(() -> Assertions.assertEquals(result,5));

多线程并发测试

在resources 目录下新建junit 配置文件 junit-platform.properties https://junit.org/junit5/docs/current/user-guide/#writing-tests-parallel-execution

# 是否允许并行测试 true/false
junit.jupiter.execution.parallel.enabled=true
# 是否允许测试方法并行执行,same_thread 同一线程执行, concurrent 多线程并发执行
junit.jupiter.execution.parallel.mode.default=concurrent
# 是否允许测试类之间的并发执行
junit.jupiter.execution.parallel.mode.classes.default = same_thread
# 配置并行策略fixed 或者dynamic 使用fixed 需要自行配置多线程并发执行数
junit.jupiter.execution.parallel.config.strategy = fixed
junit.jupiter.execution.parallel.config.fixed.parallelism = 2

在测试用例上用@RepeatTest 注解来实现并发允许

@RepeatedTest(10)
public void testCount() throws InterruptedException {
int result = Calculator.count(1);
System.out.println(result);
}

测试套件的运行以及指定测试类

参考: https://junit.org/junit5/docs/current/user-guide/#testkit-engine

在pom.xml 文件中添加依赖
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite-api</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite-engine</artifactId>
<scope>test</scope>
</dependency>



在测试类中添加@Suit 注解创建测试套
import org.junit.platform.suite.api.SelectPackages;
import org.junit.platform.suite.api.Suite;
import org.junit.platform.suite.api.SuiteDisplayName;



@Suite
@SuiteDisplayName("批量测试套")
@IncludeClassNamePatterns("*Test")
@IncludeTags({})
@SelectClasses({})
@ExcludeClassNamePatterns("")
@ExcludePackages({})
@ExcludeTags({})
@SelectPackages({"com.huawei.test"})
public class SuitDemo {
}
@Suite创建测试套件
@SuiteDisplayName测试套命名
@SelectClass()选择加入测试套的用例
@SelectPackage()选择包下面的所有用例
@IncludeClassNamePatterns("*Test")选择运行类名包含Test
@ExcludeClassNamePatterns("")排除类名符合某种规则
@ExcludePackages({})排除选择的包
@ExcludeTags({})排除被Tag 标记的测试用例

命令行 利用mvn test 和Maven Surefire plugin 插件做测试用例的执行管理

  • mvn test 执行全量的用例
  • mvn -Dtest=${TestClass} test 指定测试用例运行 如 mvn -DTest =SuitDemo test
  • mvn -Dtest= T e s t C l a s s 1 , {TestClass1}, TestClass1,{TestClass2} test 指定多个测试类运行
  • mvn -Dtest=${*TestClass} 通过匹配的方式指定测试类的运行
  • mvn -Dtest=${TestClass}#${TestMethod} 指定测试方法运行

用例参数化

public class ParamDemoTest {


@ParameterizedTest
@ValueSource(ints={1,3})
public void testCount(int x) throws InterruptedException {
int result = Calculator.count(x);
System.out.println(result);
}

@ParameterizedTest
@CsvSource(value = {"zhangsan,22,男","lisi,33,女"})
public void testCsv(String arg1 ,String arg2,String arg3){

System.out.println("arg1:" + arg1);

System.out.println("arg2:" + arg2);
System.out.println("arg3:" + arg3);
}

@ParameterizedTest
@CsvFileSource(resources = "/data/user.csv")
public void testCsvFile(String arg1 ,String arg2){

System.out.println("arg1:" + arg1);

System.out.println("arg2:" + arg2);
}

/**
* @MethodSource 指定数据源的来源方法,方法的返回值可以时Array List Stream 等可迭代的对象
*/

@ParameterizedTest
@MethodSource(value = {"getData"})
public void testMethod(String arg){
System.out.println(arg);
}

@ParameterizedTest
@MethodSource(value = {"getMap"})
public void testMethod1(Map.Entry entry){
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}


public void testGetYaml(){

}

static Iterator<Map.Entry<String, String>> getMap(){
HashMap<String,String> map = new HashMap<>();

map.put("name","zhangsan");
map.put("sex","男");
return map.entrySet().iterator();
}

static Stream<String> getData(){
return Stream.of("zhangsan","lisi");

}


@Test
public void testYaml() throws IOException {
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());

// TypeReference typeReference = new TypeReference() {
// }

TypeReference typeReference = new TypeReference<List<YamlData>>() {

};

List<YamlData> yamlData = objectMapper.readValue(this.getClass().getResourceAsStream("/data/testdata.yaml"),typeReference);

yamlData.forEach((data) ->{
System.out.println(data.username + data.age);
});

}
}

利用Jackson + yaml 实现参数化步骤:

参考链接:https://www.baeldung.com/jackson-yaml

  • pom.xml 文件中添加Jackson 的依赖包

     <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.8</version>
    </dependency>
  • 添加jackson 支持yaml的依赖包

    <dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-yaml</artifactId>
    <version>2.9.8</version>
    </dependency>
  • 编写yaml

    -
    username: zhangsan
    age: 20
    computers:
    - brand: 华为
    price: 19999
    - brand: 小米
    price: 9999.9
    action:
    eat: 大鱼大肉
    sleep: 10小时
    map:
    id: 001
    step: 向后走

    -
    username: lisi
    age: 30
    computers:
    - brand: mac
    price: 29999
    action:
    eat: 青菜萝卜
    sleep: 10小时
    map:
    id: 002
    step: 向前走


    //yaml 以缩进代表层级关系 不允许tab 以两个空格代表缩进
  • 定义对应格式的实体类,注意如果属性是私有属性提供响应get/set 方法

    @JsonIgnoreProperties(ignoreUnknown = true)
    public class YamlData {

    private String username;

    private int age;

    private List<Computer> computers;

    private Action action;

    private HashMap<String,String> map;

    //get/set 方法省略....

    }
  • 编写相应的测试类

     @Test
    public void testYaml() throws IOException {
    ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());

    // TypeReference typeReference = new TypeReference() {
    // }

    TypeReference typeReference = new TypeReference<List<YamlData>>() {

    };

    List<YamlData> yamlData = objectMapper.readValue(this.getClass().getResourceAsStream("/data/testdata.yaml"),typeReference);

    yamlData.forEach((data) ->{
    System.out.println(data.username + data.age);
    });

    }
    }
举报

相关推荐

0 条评论