如何开始使用Java进行单元测试:JUnit 5的完整介绍

您好Dev.to! 在本文中,我想重点介绍使用JUnit5库进行Java中的单元测试。 与较早的JUnit 4版本相比,它引入了新的测试功能和方法,值得检查。 我们将概述什么是单元测试以及为什么要测试; 如何在项目中安装JUnit 5; 什么是基本测试结构? 如何使用Asseritions API以及如何在测试套件中组合多个测试。

Btw, original post was published in my blog and you can find it here

What is unit testing?

单元测试当我们单独测试各个软件的组件时,它是软件测试的级别。 例如,我们有用户服务。 它可能具有各种连接的依赖关系,例如用户DAO连接到数据源,或者邮件提供商发送确认电子邮件。 但是对于单元测试,我们隔离用户服务并且可能嘲笑 connected dependencies (how to do 嘲笑ing, we would see in the next chapter).

单元测试为我们提供了许多好处,仅举几例:

  • 当我们更改代码时,它增加了我们的信心。 如果单元测试编写良好,并且每次更改任何代码都可以运行它们,那么在引入新功能时我们会注意到任何失败它用作文档。 总而言之,记录您的代码包括多种工具,而单元测试就是其中之一-它描述了预期您的代码对其他开发人员的行为。它使您的代码更多可重用,至于良好的单元测试,代码组件应该模块化。

这些优势仅是单元测试所提供的众多优势中的少数。 现在,当我们定义什么是单元测试以及为什么要使用它时,我们已经准备好迁移到JUnit5。

Install JUnit5

Build tools support

对于JUnit5的本机支持,您应该具有Gradle 4.6+或Maven 2.22.0+的版本。

对于Maven,您需要添加到Pom.xml:

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>{$version}</version>
    <scope>test</scope>
</dependency>

对于Gradle添加到build.gradle:

testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '$version'

You could find the latest version of JUnit5 in the official repository. By the way, if you would use JUnit 5 with Vert.x framework, there is Vert.x JUnit5 extension.

IDE support

自2016.2起,Intellij IDEA就本机支持JUnit5,自4.7.1a起便支持Eclipse。

An anatomy of unit test

Basic structure

Consider this example: we have a program, that performs a linear search of a number in an array of integers. Here is a main class, that we place in the src/main/java/ folder:

class LinearSearcher(){

    private int[] data;

   LinearSearcher(int[] arr){
      this.data = arr;
   }

   int getPositionOfNumber(int value){
      int n = data.length; 
      for(int p = 0; i < n; i++) 
         { 
           if(data[p] == value) 
            return p; 
          } 
      return -1; 
   }
}

然后将第二个代码添加到src /测试/ java夹:

class LinearSearcherTest{

    private static LinearSearcher searcher;

    //before
    @BeforeAll
    static void setup(){
       int[] array = {2, 3, 4, 10, 40};
       searcher = new LinearSearcher(array);
    }

    //Actual test methods
    @Test
    void getPosition(){
       int result = searcher.getPositionOfNumber(10);
       Assertions.assertEquals(3,result);
    }

    @Test
    void noSuchNumber(){
       int result = searcher.getPositionOfNumber(55);
       Assertions.assertEquals(-1, result);
    }

    //after
    @AfterAll
    static void finish(){
       System.out.println("Tests are finished!");
    }
}

让我们检查一下我们在这段代码中做了什么。 我们引入了一个新类,LinearSearcher有一种方法-getPostionOfNumber返回值在数组中的位置,如果值未在数组中返回,则返回-1。

二等班LinearSearcherTest我们实际上是进行单元测试。 我们希望有两种情况:当数组中有一个数字(在本例中为10)时,我们期望收到其位置(3)。 如果没有提供这样的数字(例如55),我们的搜索者应该返回-1。 现在,您可以运行此代码并检查结果。

Before methods

您可能会注意到分别用注释的两种方法@BeforeAll和@毕竟。 他们在做什么? 第一种方法对应之前的方法。 其中有两个:

  • @BeforeAll - the static method that will be executed once before all @test method in the current class.
  • @BeforeEach - the method that will be executed before each @test method in the current class.

这些方法很容易设置单元测试环境(例如,创建实例)。

After methods

由于有方法之前, 有后方法。 还有一些:

  • @AfterAll - the static method will be executed once after all @test methods in the current class.
  • @AfterEach - the method that will be executed after each @test method in the current class.

Using standard Assertions API

断言API是支持测试中声明条件的实用程序方法的集合。 有许多可用的方法,但是我们将重点介绍其中最重要的方法。

Assert not null

当我们需要断言时实际对象不为null,我们可以使用以下方法:

assertNotNull(Object obj);

如果object不为null,则该方法通过,否则返回-失败。

Assert Equals

This group includes many methods, so I would not provide your all overloaded versions, but would focus a general signature:

assertEquals(expected_value, actual_value, optional_message);

这些方法有两种必选参数还有一个可选参数:

  • Expected_value =我们要接收的结果Actual_value =测试值optional_ message =字符串消息,如果方法失败,它将显示给STDOUT。

值可以是原始类型:int,double,float,long,short,boolean,char,byte以及Strings和Objects。 我们可以向这些组添加以下测试方法:

  • assertArrayEquals - check that expected and actual arrays are equal. Arrays are of primitive types
  • AssertFalse and AssertTrue - check that supplied boolean condition is false or true respectively
  • assertIterableEquals - same as assertArrayEquals, but for Iterables (e.g. List, Set etc)

As I mentioned, there are many overloaded methods in this section, so it worth to explore official documentation for concrete signatures.

Assert throws

这是JUnit5的一项创新。 考虑一下,您有一个引发异常的方法:

Car findCarById(String id) throws FailedProviderException;

此方法检索个人汽车从底层数据库按其ID进行操作,并在数据库出现问题时引发FailedProviderException。 换句话说,我们在接口中包装可能的数据源异常(例如SQLException或受NoSQL数据库尊重),并实现其与实现的独立性。

我们如何测试抛出异常? 之前,在JUnit4中,我们使用了注释:

@Test(expected = FailedProviderException.class)
void exceptionThrownTest() throws Exception{
    Car result = repository.findCarById("non-existing-id");
}

顺便说一句,在TestNG中使用了相同的想法。 在JUnit5中被引入assertThrows方法。 看一下我们如何处理相同的情况:

@Test
void exceptionThrownTest(){
    Assertions.assertThrows(FailedProviderException.class, ()->{
        Car result = repository.findCarById("non-existing-id");
    });
}

此方法签名包含两个组件:

  1. Expected exception to be thrown
  2. Lambda expression of Executable, that contains a code snippet, that potentially throws the exception.

再次,正如前面提到的assertEquals分组方法一样,我们可以提供String的可选消息作为第三个参数。

Assert timeout

当需要断言测试在定义的超时内完成时,可以使用以下方法:

assertTimeout(Duration timeout, Executable executable)

这个想法与assertThrows方法相同,但是我们在此处指定超时。 第二个参数是相同的可执行lambda表达式。 第三个可选组件是字符串消息。 让我们考虑一个例子:

@Test
void in3secondsTest(){
   Assertions.assertTimeout(Duration.ofSeconds(3), ()->{
      //some code
   });
}

Please note, that this method uses Duration API to specify timeframe. It has several handy methods, like ofSeconds(), ofMills() etc. If you are not familiar with it, don't be shy to check this tutorial.

Fail

最后,如果我们需要失败测试该怎么办? 只需使用Assertions.fail()方法。 同样,其中有几个:

  • fail(字符串消息)=使用给定的失败消息使测试失败。失败(字符串消息,可抛出原因)=使用给定的失败消息以及潜在原因导致测试失败。失败(可抛出原因)=使用给定的根本原因导致测试失败。

Creating test suites

如果您有多个单元测试,并且想要一次加载执行它们,则可以创建一个测试套件。

这种方法使您可以运行分散到多个测试类和不同包中的测试。

假设我们有测试TestA,TestB,TestC,它们分为三个包:net.mednikov.teststutorial.groupA,net.mednikov.teststutorial.groupA,net.mednikov.teststutorial.groupC。 我们可以编写测试套件来组合它们:

@RunWith(JUnitPlatform.class)
@SelectPackages({net.mednikov.teststutorial.groupA, net.mednikov.teststutorial.groupB, net.mednikov.teststutorial.groupC})
public class TestSuite(){}

现在,您可以将此方法作为一个测试套件运行。

References

  • Sergio Martin. Take Unit Testing to the Next Level With JUnit 5 (2018). DZone, read here
  • Petri Kainulainen. Writing Assertions with JUnit5 Assertion API (2018), read here
  • J Steven Perry. The JUnit5 Jupiter API (2017) IBM Developer, read here

Conclusion

In this post, we learned what is unit test and why to test; how to install JUnit 5 in your project; what is a basic test structure; how to use Asseritions API and how to combine multiple tests from different packages in a test suite. But, of course, JUnit 5 is a huge topic, and this post is just a top of an iceberg. Some frameworks, like Vert.x, offer special JUnit 5 extensions, like vertx-junit5. Good luck with JUnit5 ! :)

from: https://dev.to//andreevich/how-to-start-with-unit-testing-in-java-a-complete-introduction-to-junit-5-3cc

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值