junit mockito_JUnit 5教程,第1部分:使用JUnit 5,Mockito和Hamcrest进行单元测试

junit mockito

JUnit 5是用于用Java开发单元测试的新的事实上的标准。 这个最新版本摆脱了Java 5的限制,并集成了Java 8的许多功能,其中最著名的是对lambda表达式的支持。

在由两部分组成的JUnit 5简介的前半部分中,您将开始使用JUnit 5进行测试。我将向您展示如何配置Maven项目以使用JUnit 5,如何使用@Test@ParameterizedTest批注以及如何在JUnit 5中使用新的生命周期批注。您还将看到一个使用过滤器标签的简短示例,并且我将向您展示如何将JUnit 5与第三方断言库集成在一起。这种情况,哈姆克雷斯特。 最后,您将获得一个快速的教程介绍,以将JUnit 5与Mockito集成在一起,以便您可以为复杂的实际系统编写更强大的单元测试。

下载
获取代码
获取本教程中示例的源代码。 由Steven Haines为JavaWorld创建。

测试驱动的开发

如果您已经开发Java代码已有一段时间,那么您可能对测试驱动的开发非常熟悉,因此我将在本节中保持简短。 重要的是要理解为什么我们编写单元测试,以及开发人员在设计单元测试时采用的策略。

测试驱动开发(TDD)是将编码,测试和设计交织在一起的软件开发过程。 这是一种测试优先的方法,旨在提高您的应用程序的质量。 测试驱动的开发由以下生命周期定义:

  1. 添加测试。
  2. 运行所有测试,并观察新测试失败。
  3. 实施代码。
  4. 运行所有测试,然后观察新测试。
  5. 重构代码。

图1显示了该TDD生命周期。

junit mockito_JUnit 5教程,第1部分:使用JUnit 5,Mockito和Hamcrest进行单元测试_第1张图片 史蒂文·海恩斯

图1.测试驱动的开发生命周期

在编写代码之前编写测试有两个目的。 首先,它迫使您考虑要解决的业务问题。 例如,成功方案应如何表现? 什么条件应该失败? 他们应该如何失败? 其次,测试首先使您对测试更有信心。 每当我在编写代码之后编写测试时,我总是必须破坏它们以确保它们确实捕获了错误。 首先编写测试可以避免此额外步骤。

为幸福的道路编写测试通常很容易:给定好的输入,该类应返回确定性的响应。 但是编写负面(或失败)的测试用例,尤其是对于复杂的组件,可能会更加复杂。

例如,考虑为数据库存储库编写测试。 在快乐的道路上,我们将一条记录插入数据库,并接收回创建的对象,包括任何生成的键。 实际上,我们还必须考虑发生冲突的可能性,例如插入一条记录,该记录具有另一个记录已经拥有的唯一列值。 此外,当存储库无法连接到数据库(可能是因为用户名或密码已更改)时,会发生什么? 如果在传输过程中出现网络错误怎么办? 如果请求未在您定义的超时限制内完成,该怎么办?

为了构建一个健壮的组件,您需要考虑所有可能和不太可能的场景,为它们开发测试,并编写代码来满足这些测试。 在本文的后面,我们将研究创建不同故障场景的策略,以及JUnit 5中的一些新功能,这些功能可以帮助您测试这些场景。

采用JUnit 5

如果您已经使用JUnit一段时间,则JUnit 5中的一些更改将是一种调整。 以下是这两个版本之间的区别的简要概述:

  • 现在,JUnit 5被打包在org.junit.jupiter组中,该组更改了将其包含在Maven和Gradle项目中的方式。
  • JUnit 4要求最低的JDK为JDK 5; JUnit 5至少需要JDK 8。
  • JUnit 4的@Before@BeforeClass@After@AfterClass批注已被替换@BeforeEach@BeforeAll@AfterEach@AfterAll ,分别。
  • JUnit 4的@Ignore注解已被取代的@Disabled注释。
  • @Category注释已被@Tag注释替换。
  • JUnit 5添加了一组新的断言方法。
  • 跑步者已被扩展所取代,并为扩展实现者提供了新的API。
  • JUnit 5引入了使测试无法执行的假设。
  • JUnit 5支持嵌套和动态测试类。

我们将在本文中探索大多数这些新功能。

使用JUnit 5进行单元测试

让我们从一个简单的端到端示例开始,配置一个项目以使用JUnit 5进行单元测试。 清单1显示了MathTools类,该类的方法将分子和分母转换为double

清单1.一个示例JUnit 5项目(MathTools.java)


package com.javaworld.geekcap.math;

public class MathTools {
    public static double convertToDecimal(int numerator, int denominator) {
        if (denominator == 0) {
            throw new IllegalArgumentException("Denominator must not be 0");
        }
        return (double)numerator / (double)denominator;
    }
}

我们有两种主要的场景来测试MathTools类及其方法:

  • 一个有效的测试 ,其中我们为分子和分母传递非零整数。
  • 一个失败场景 ,其中我们为分母传递零值。

清单2显示了一个JUnit 5测试类来测试这两种情况。

清单2.一个JUnit 5测试类(MathToolsTest.java)


package com.javaworld.geekcap.math;

import java.lang.IllegalArgumentException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

class MathToolsTest {
    @Test
    void testConvertToDecimalSuccess() {
        double result = MathTools.convertToDecimal(3, 4);
        Assertions.assertEquals(0.75, result);
    }

    @Test
    void testConvertToDecimalInvalidDenominator() {
        Assertions.assertThrows(IllegalArgumentException.class, () -> MathTools.convertToDecimal(3, 0));
    }
}

在清单2中, testConvertToDecimalInvalidDenominator方法在assertThrows调用内执行MathTools::convertToDecimal方法。 第一个参数是要引发的异常的预期类型。 第二个参数是将引发该异常的函数。 assertThrows方法执行该函数并验证是否抛出了预期的异常类型。

断言类及其方法

org.junit.jupiter.api.Test注释表示一种测试方法。 请注意,@ @Test注释现在来自JUnit 5 Jupiter API包,而不是JUnit 4的org.junit包。 testConvertToDecimalSuccess方法首先执行分子为3且分母为4的MathTools::convertToDecimal方法,然后断言结果等于0.75。 org.junit.jupiter.api.Assertions类提供了一组用于比较实际结果和预期结果的static方法。 Assertions类具有以下方法,这些方法涵盖了大多数原始数据类型:

  • assertArrayEquals将实际数组的内容与预期数组进行比较。
  • assertEquals将实际值与期望值进行比较。
  • assertNotEquals比较两个值以验证它们不相等。
  • assertTrue验证提供的值为true。
  • assertFalse验证提供的值为false。
  • assertLinesMatch比较两个String列表。
  • assertNull验证提供的值为null。
  • assertNotNull验证提供的值不为null。
  • assertSame验证两个值引用相同的对象。
  • assertNotSame验证两个值未引用同一对象。
  • assertThrows验证方法的执行将引发预期的异常(您可以在上面的testConvertToDecimalInvalidDenominator示例中看到此异常)。
  • assertTimeout验证提供的函数在指定的超时内完成。
  • assertTimeoutPreemptively验证提供的函数是否在指定的超时内完成,但是一旦达到超时, assertTimeoutPreemptively该函数的执行。

如果这些声明方法中的任何一个失败,则将单元测试标记为失败。 运行测试时,该故障通知将写入屏幕,然后保存在报告文件中。

在assertEquals中使用增量

assertEquals使用floatdouble值时,您还可以指定一个代表两者之间差异阈值的delta 在我们的示例中,我们可以添加0.001的增量,以防0.75实际上返回为0.750001。

分析测试结果

assert方法除了验证值或行为外,还可以接受错误的文本描述,这可以帮助您诊断故障。 例如:


Assertions.assertEquals(0.75, result, "The MathTools::convertToDecimal value did not return the correct value of 0.75 for 3/4");
Assertions.assertEquals(0.75, result, () -> "The MathTools::convertToDecimal value did not return the correct value of 0.75 for 3/4");

输出将显示期望值0.75和实际值。 它还将显示指定的消息,这可以帮助您了解错误的上下文。 两种变体之间的区别是,第一个变体始终创建消息,即使未显示该消息,而第二个变体仅在断言失败时构造消息。 在这种情况下,消息的构造是微不足道的,因此并不重要。 尽管如此,仍不需要为通过的测试构造错误消息,因此通常最好的方法是使用第二种样式。

最后,如果您使用的是像IntelliJ这样的IDE来运行测试,则每个测试方法将按其方法名称显示。 如果您的方法名称可读,那么可以,但是您也可以在测试方法中添加@DisplayName批注,以更好地识别测试:

@Test
@DisplayName("Test successful decimal conversion")
void testConvertToDecimalSuccess() {
  double result = MathTools.convertToDecimal(3, 4);
  Assertions.assertEquals(0.751, result);
}

运行单元测试

为了从Maven项目运行JUnit 5测试,您需要在Maven pom.xml文件中包括maven-surefire-plugin并添加新的依赖项。 清单3显示了此项目的pom.xml文件。

清单3.一个示例JUnit 5项目的Maven pom.xml



      4.0.0
      com.javaworld.geekcap
      junit5
      jar
      1.0-SNAPSHOT
      
          
              
                  org.apache.maven.plugins
                  maven-compiler-plugin
                  3.8.1
                  
                      8
                      8
                  
              
              
                  org.apache.maven.plugins
                  maven-surefire-plugin
                  3.0.0-M4
              
          
      
      junit5
      http://maven.apache.org
      
          
              org.junit.jupiter
              junit-jupiter
              5.6.0
              test
          
      
  

JUnit 5依赖项

JUnit 5将其组件打包在org.junit.jupiter组中,我们需要添加junit-jupiter工件,这是一个聚合器工件,它会导入以下依赖项:

  • junit-jupiter-api定义用于编写测试和扩展的API。
  • junit-jupiter-engine是运行单元测试的测试引擎实现。
  • junit-jupiter-params为参数化测试提供支持。

接下来,我们需要添加maven-surefire-plugin构建插件来运行测试。

最后,确保将maven-compiler-plugin包含在Java 8或更高版本中,以便您能够使用Java 8功能(例如lambda)。

运行!

使用以下命令从您的IDE或Maven运行测试类:

mvn clean test

如果成功,则应该看到类似以下的输出:


[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.javaworld.geekcap.math.MathToolsTest
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.04 s - in com.javaworld.geekcap.math.MathToolsTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  3.832 s
[INFO] Finished at: 2020-02-16T08:21:15-05:00
[INFO] ------------------------------------------------------------------------

翻译自: https://www.infoworld.com/article/3537563/junit-5-tutorial-part-1-unit-testing-with-junit-5-mockito-and-hamcrest.html

junit mockito

你可能感兴趣的:(junit mockito_JUnit 5教程,第1部分:使用JUnit 5,Mockito和Hamcrest进行单元测试)