Java单元测试框架(二)——JUnit5

简介: Java单元测试框架(二)——JUnit5

1.原理图


image.png


2. 基本架构


被测代码

package com.jerry;
public class Calculator{
    private static int result; 
    public void add(int n) {
        result = result + n;
    }
    public void substract(int n) {
        result = result - n;  
    }
    public void multiply(int n) {
        result = result * n;
    } 
    public void divide(int n){
        try {
            result = result / n;
        }catch(ArithmeticException ex){
            System.out.println(ex);
            throw new ArithmeticException("The n not allowed to 0!!")
        }
}
}


最基本的测试代码

public class CalculatorTest {
    private static Calculator calculator = new Calculator();
    @BeforeEach
    public void setUp() throws Exception {
        calculator.clear();
    }
    @Test
    @DisplayName("测试加法")
    public void testAdd() {
        calculator.add(2);
        calculator.add(3);
        assertEquals(5, calculator.getResult());
    }
    @Test
    @DisplayName("测试减法")
    public void testSubstract() {
        calculator.add(5);
calculator.substract(3);
        Assertions.assertEquals(2, calculator.getResult());
    }
    @Test
    @DisplayName("测试乘法")
    public void testMultiply() {
        calculator.add(3);
        calculator.multiply(2);
        Assertions.assertEquals(6, calculator.getResult());
    }
    @Test
    @DisplayName("测试除法")
    public void testDivide() {
        calculator.add(9);
        calculator.divide(3);
        Assertions.assertEquals(3, calculator.getResult());
    }


标签@DisplayName可以在测试结果的时候显示其内容。

 

image.png


3.JUnit5的修饰符


修饰符

含义

@DisplayName

为测试类或者测试方法设置展示名称

@BeforeAll

表示在所有单元测试之前执行

@AfterAll

表示在所有单元测试之后执行

@BeforeEach

表示在每个单元测试之前执行

@AfterEach

表示在每个单元测试之后执行

@Disabled

表示测试类或测试方法不执行,类似于JUnit4中的@Ignore

@Timeout 

表示测试方法运行如果超过了指定时间将会返回错误,类似于JUnit4中的(timeout=XXX) JUnit 5.5.2以后

@RepeatedTest

表示方法可重复执行

@ParameterizedTest 

表示方法是参数化测试,类似于JUnit4中的@RunWith(Parameterized.class)

@Tag

表示单元测试类别,类似于JUnit4中的@Categories

@ExtendWith

为测试类或测试方法提供扩展类引用


描述装饰符的程序

package com.jerry;
import static org.junit.jupiter.api.Assertions.assertAll;
import org.junit.jupiter.api.*;
public class MyFirstJunit5Test {
  @BeforeAll
    @DisplayName("每条用例开始时执行")
  public static void startALL(){
    System.out.println("BeforeAll");
    }
  @AfterAll
    @DisplayName("每条用例开始时执行")
  public static void endAll(){
    System.out.println("AfterAll");
    }
  @BeforeEach
    @DisplayName("每条用例开始时执行")
    void start(){
    System.out.println("BeforeEach");
    }
    @AfterEach
    @DisplayName("每条用例结束时执行")
    void end(){
      System.out.println("AfterEach");
    }
    @Test
    void myFirstTest() {
      System.out.println("myFirstTest");
      Assertions.assertEquals(2, 1 + 1);
    }
    @Test
    @DisplayName("描述测试用例")
    void testWithDisplayName() {
      System.out.println("testWithDisplayName");
    }
    @Test
    @Disabled("这条用例暂时跑不过,忽略!")
    void myFailTest(){
      System.out.println("Disabled Testcase");
      Assertions.assertEquals(1,2);
    }
    @Test
    @DisplayName("运行一组断言")
    public void assertAllCase() {
      System.out.println("运行一组断言");
        assertAll("groupAssert",
                () -> Assertions.assertEquals(2, 1 + 1),
                () -> Assertions.assertTrue(1 > 0)
        );
    }
    @Test
    @DisplayName("依赖注入1")
    public void testInfo(final TestInfo testInfo) {
        System.out.println(testInfo.getDisplayName());
    }
    @Test
    @DisplayName("依赖注入2")
    public void testReporter(final TestReporter testReporter) {
        testReporter.publishEntry("name", "Alex");
    }
}


运行结果(缩进为了看起来方便,我自己设置的)

BeforeAll
     BeforeEach
        依赖注入1
    AfterEach
    BeforeEach
        TestIdentifier [依赖注入2]
         ReportEntry [timestamp = 2020-08-21T11:38:49.361976500, name = 'Alex’]
     AfterEach
    BeforeEach
        myFirstTest
    AfterEach
    BeforeEach
         testWithDisplayName
    AfterEach
    BeforeEach
       运行一组断言
    AfterEach
AfterAll


注意:@BeforeAll、 @AfterALL注解方法必须是静态方法,否则会抛出运行时错误。


4. JUnit5 新加断言


断言方法

断言描述

assertTimeoutPreemptively

超时断言

assertThrows

异常断言


5. 异常断言


被测的程序

public void divide(int n){
      try {
        result = result / n;
      }catch(ArithmeticException ex){
        System.out.println(ex);
        throw new ArithmeticException("The n not allowed to 0!!");
      }
    }


测试程序

@Test
    @DisplayName("测试除0异常")
    public void testDivideByZero() {
        calculator.add(9);
        Throwable exception = Assertions.assertThrows(ArithmeticException.class, () -> calculator.divide(0));
        Assertions.assertEquals("The n not allowed to 0!!",exception.getMessage());
  }


6. 超时断言


@Test
    public void squareRoot()  {
       Assertions.assertTimeoutPreemptively(Duration.of(2, ChronoUnit.SECONDS), () -> calculator.squareRoot(4))
}


判断程序是否应该在两秒钟执行完毕。类似于JUnit4中的(timeout=XXX)。


7. 参数化测试


7.1单参数

@ParameterizedTest
@ValueSource(ints = {1,2,3,4})
@DisplayName("参数化测试_单参数")
public void parameterizedTest(int param) {
    calculator.add(param);
    calculator.add(-1 * param);
    Assertions.assertEquals(0, calculator.getResult());
}


标签:

  • @ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
  • @NullSource: 表示为参数化测试提供一个null的入参
  • @EnumSource: 表示为参数化测试提供一个枚举入参


ValueSource类型:

  • String values: @ValueSource(strings = {"foo", "bar", "baz"})
  • Double values: @ValueSource(doubles = {1.5D, 2.2D, 3.0D})
  • Long values: @ValueSource(longs = {2L, 4L, 8L})
  • Integer values: @ValueSource(ints = {2, 4, 8})


7.2 Enum参数

import java.util.concurrent.TimeUnit;
@ParameterizedTest
@EnumSource(value = TimeUnit.class, names = {"SECONDS", "MINUTES"})
@DisplayName("参数化测试_Enum参数")
void testTimeUnitJustSecondsAndMinutes(TimeUnit unit) {
Assertions.assertTrue(EnumSet.of(TimeUnit.SECONDS, TimeUnit.MINUTES).contains(unit));
Assertions.assertFalse(EnumSet
          .of(TimeUnit.DAYS, TimeUnit.HOURS, TimeUnit.MILLISECONDS, TimeUnit.NANOSECONDS,
                TimeUnit.MICROSECONDS).contains(unit));
}


7.3参数化测试——方法参数(多参数)

注意:这个方法返回必须是个流
    @ParameterizedTest
    @MethodSource("paramGenerator")
    @DisplayName("参数化测试_ 方法参数(多参数)")
    void MethodParameForSquareRoot(int param, int result){    
        calculator.squareRoot(param);
        Assertions.assertEquals(result, calculator.getResult());
    }
    static Stream<Arguments> paramGenerator(){
        return Stream.of(Arguments.of(4,2), Arguments.of(9,3), Arguments.of(16,4));
    }


7.4.参数化测试——CVS文件参数  

@ParameterizedTest
    @CsvFileSource(resources = "data.csv")  //指定csv文件位置
    @DisplayName("参数化测试-csv文件")
    public void parameterizedCVSFile(int param, int result) {
        calculator.squareRoot(param);
        Assertions.assertEquals(result, calculator.getResult());
    }


其中,data.csv为:

4  2
9  3
16  4


8.内嵌测试类


一般一个产品类对应一个测试类,但是使用JUnit,可以实现类的嵌套。


package com.jerry;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
public class NestedTestDemo {
    @Test
    @DisplayName("Nested")
    void isInstantiatedWithNew() {
        System.out.println("最一层--内嵌单元测试");
    }
    @Nested
    @DisplayName("Nested2")
    class Nested2 {
        @BeforeEach
        void Nested2_init() {
            System.out.println("Nested2_init");
        }
        @Test
        void Nested2_test() {
            System.out.println("第二层-内嵌单元测试");
        }
        @Nested
        @DisplayName("Nested3")
        class Nested3 {
            @BeforeEach
            void Nested3_init() {
                System.out.println("Nested3_init");
            }
            @Test
            void Nested3_test() {
                System.out.println("第三层-内嵌单元测试");
            }
        }
    }
}


结果输出:

第一层--内嵌单元测试
Nested2_init
第二层-内嵌单元测试
Nested2_init
Nested3_init
第三层-内嵌单元测试

image.png


9. 重复测试


@RepeatedTest(5) //表示重复执行5次
@DisplayName("重复减法")
public void testSubstractManyTimes() {
    calculator.add(5);
    calculator.substract(3);
    Assertions.assertEquals(2, calculator.getResult());
}


这个测试用例将被执行5次,为什么设计这个方法,我个人没有理解。


10. 动态测试


TestFactory
@DisplayName("动态测试")
Iterator<DynamicTest> dynamicTests() {
    return Arrays.asList(
        dynamicTest("第1个动态测试", () ->{calculator.squareRoot(4);Assertions.assertEquals(2, calculator.getResult());}),
        dynamicTest("第2个动态测试", () ->{calculator.squareRoot(9);Assertions.assertEquals(3, calculator.getResult());}),
        dynamicTest("第3个动态测试", () ->{calculator.squareRoot(16);Assertions.assertEquals(4, calculator.getResult());}),
        dynamicTest("第4个动态测试", () ->{calculator.squareRoot(25);Assertions.assertEquals(5, calculator.getResult());})
    ).iterator();
 }


动态测试用得比较少,这个功能的原因我个人也不太理解。


11分组断言assertAll


@Test
@DisplayName("开根号分组断言")
public void testGroup() {
    int[] parem = {4, 9, 16, 25, 36};
    int[] result ={2, 3, 4, 5, 6};
    Assertions.assertAll("parem,result",
        () -> {calculator.squareRoot(parem[0]);Assertions.assertEquals(result[0], calculator.getResult());},
        () -> {calculator.squareRoot(parem[1]);Assertions.assertEquals(result[1], calculator.getResult());},
        () -> {calculator.squareRoot(parem[2]);Assertions.assertEquals(result[2], calculator.getResult());},
        () -> {calculator.squareRoot(parem[3]);Assertions.assertEquals(result[3], calculator.getResult());},
        () -> {calculator.squareRoot(parem[4]);Assertions.assertEquals(result[4], calculator.getResult());}
   );


分组断言中任一个断言的失败,都会将以 MultipleFailuresError 错误进行抛出提示。另外可以看出,使用分组断言也可以实现参数化

目录
相关文章
|
4月前
|
Java 数据库
在Java中使用Seata框架实现分布式事务的详细步骤
通过以上步骤,利用 Seata 框架可以实现较为简单的分布式事务处理。在实际应用中,还需要根据具体业务需求进行更详细的配置和处理。同时,要注意处理各种异常情况,以确保分布式事务的正确执行。
|
3月前
|
存储 安全 Java
Java 集合框架中的老炮与新秀:HashTable 和 HashMap 谁更胜一筹?
嗨,大家好,我是技术伙伴小米。今天通过讲故事的方式,详细介绍 Java 中 HashMap 和 HashTable 的区别。从版本、线程安全、null 值支持、性能及迭代器行为等方面对比,帮助你轻松应对面试中的经典问题。HashMap 更高效灵活,适合单线程或需手动处理线程安全的场景;HashTable 较古老,线程安全但性能不佳。现代项目推荐使用 ConcurrentHashMap。关注我的公众号“软件求生”,获取更多技术干货!
62 3
|
1月前
|
存储 缓存 Java
java语言后台管理ruoyi后台管理框架-登录提示“无效的会话,或者会话已过期,请重新登录。”-扩展知识数据库中密码加密的方法-问题如何解决-以及如何重置若依后台管理框架admin密码-优雅草卓伊凡
java语言后台管理ruoyi后台管理框架-登录提示“无效的会话,或者会话已过期,请重新登录。”-扩展知识数据库中密码加密的方法-问题如何解决-以及如何重置若依后台管理框架admin密码-优雅草卓伊凡
145 3
java语言后台管理ruoyi后台管理框架-登录提示“无效的会话,或者会话已过期,请重新登录。”-扩展知识数据库中密码加密的方法-问题如何解决-以及如何重置若依后台管理框架admin密码-优雅草卓伊凡
|
2天前
|
存储 并行计算 Java
java 中的fork join框架
Java中的Fork Join框架于Java 7引入,旨在提升并行计算能力。它通过“分而治之”的思想,将大任务拆分为多个小任务(fork),再将结果合并(join)。核心组件包括:ForkJoinPool(管理线程池和工作窃取机制)、ForkJoinWorkerThread(执行具体任务的工作线程)和ForkJoinTask(定义任务逻辑,常用子类为RecursiveAction和RecursiveTask)。框架支持通过invoke、fork/join等方式提交任务,广泛应用于高性能并发场景。
|
13天前
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
|
2月前
|
并行计算 算法 Java
Java中的Fork/Join框架详解
Fork/Join框架是Java并行计算的强大工具,尤其适用于需要将任务分解为子任务的场景。通过正确使用Fork/Join框架,可以显著提升应用程序的性能和响应速度。在实际应用中,应结合具体需求选择合适的任务拆分策略,以最大化并行计算的效率。
66 23
|
4月前
|
存储 缓存 安全
Java 集合框架优化:从基础到高级应用
《Java集合框架优化:从基础到高级应用》深入解析Java集合框架的核心原理与优化技巧,涵盖列表、集合、映射等常用数据结构,结合实际案例,指导开发者高效使用和优化Java集合。
88 4
|
4月前
|
测试技术 开发者 UED
探索软件测试的深度:从单元测试到自动化测试
【10月更文挑战第30天】在软件开发的世界中,测试是确保产品质量和用户满意度的关键步骤。本文将深入探讨软件测试的不同层次,从基本的单元测试到复杂的自动化测试,揭示它们如何共同构建一个坚实的质量保证体系。我们将通过实际代码示例,展示如何在开发过程中实施有效的测试策略,以确保软件的稳定性和可靠性。无论你是新手还是经验丰富的开发者,这篇文章都将为你提供宝贵的见解和实用技巧。
|
7月前
|
JSON Dubbo 测试技术
单元测试问题之增加JCode5插件生成的测试代码的可信度如何解决
单元测试问题之增加JCode5插件生成的测试代码的可信度如何解决
77 2
单元测试问题之增加JCode5插件生成的测试代码的可信度如何解决
|
6月前
|
IDE 测试技术 持续交付
Python自动化测试与单元测试框架:提升代码质量与效率
【9月更文挑战第3天】随着软件行业的迅速发展,代码质量和开发效率变得至关重要。本文探讨了Python在自动化及单元测试中的应用,介绍了Selenium、Appium、pytest等自动化测试框架,以及Python标准库中的unittest单元测试框架。通过详细阐述各框架的特点与使用方法,本文旨在帮助开发者掌握编写高效测试用例的技巧,提升代码质量与开发效率。同时,文章还提出了制定测试计划、持续集成与测试等实践建议,助力项目成功。
131 5

热门文章

最新文章