This upgrade your version of JUnit basic introduction to the --JUnit5

Bowen because of the author's knowledge and insights, inevitably there will be omissions mistakes, please understand.

Please indicate the source: www.sshenzx.com Thank you ~

Foreword

Once upon a time long, long time ... not to write technical articles, and today it came to write an article about the unit testing. The reason to write this article because it is next door to see a group of colleagues write unit tests a few months ago, the spot was stunning. The first time that the original test unit can also be so written, compared to my previous writing test is simply disgusting pile of own use. So they carefully study the project team JUnit5 lot of use, summarizes some of the new features, I hope one day I could write code that amazing bar.

JUnit5 Introduction

JUnit as currently the most popular areas in the Java unit testing framework has gone through several decades. The updated version of the JUnit5 stopped three years JUnit4 finally released in 2017.

As the latest version of JUnit framework, JUnit5 previous version of Junit framework are very different. First Junit5 several different sub modules from three different compositions.

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

Platform JUnit : Junit Platform JVM is started on the basis of test framework, not only to support Junit homemade test engine, other test engine can also be access.

Jupiter JUnit : JUnit Jupiter offers a new programming model JUnit5 is the core JUnit5 new features. Interior
contains a test engine for running on Junit Platform.

Vintage JUnit : Because JUint has been developed for many years, to take care of the old project, JUnit Vintage provide a compatible JUnit4.x, Junit3.x test engine.

Through the above description, I do not know if I have found JUint5 seems no longer content to quietly do a unit test framework, and it's great ambition, want to access a different test engine to support the use of various types of testing framework, become a platform for unit testing. It also uses a layered architecture, the platform layer is divided into, engine layer, a frame layer. The following diagram can be clearly reflected:

Here Insert Picture Description
As long realized the JUnit test engine interface, any testing framework can run on JUnit Platform, which represents JUnit5 will have a strong scalability.

JUnit5 open a project it!

To test the frame relatively complex compared to the previous introduction JUnit5, three modules need to introduce a jar. As the current projects are built by the gradle, the following configurations are all gradle configuration.

    testCompile("org.junit.platform:junit-platform-launcher:1.6.0")
    testCompile("org.junit.jupiter:junit-jupiter-engine:5.6.0")
    testCompile("org.junit.vintage:junit-vintage-engine:5.6.0")

Of course, you may feel that lead so many packages too complicated, it does not matter, the latest version of Junit5 have considered this point. Now only need to introduce a package to the following

   testCompile("org.junit.jupiter:junit-jupiter:5.6.0")

Oh, right. Do not forget our JUnit5 is running on JUnit Platform, it is also necessary to add this in build.gradle in. (All stepped pit ...)

test {
    useJUnitPlatform()
}

A means to open the first test after the introduction JUnit5. Note @Test annotation using org.junit.jupiter.api.Test under the package, and then to do a version of Junit4.

import org.junit.jupiter.api.Test; //注意这里使用的是jupiter的Test注解!!


public class TestDemo {

  @Test
  @DisplayName("第一次测试")
  public void firstTest() {
      System.out.println("hello world");
  }

Basic annotations

JUnit5 annotations and notes JUnit4 of change, notes listed below as part of the common comment I think

annotation Explanation
@Test Representation is the test method. But with JUnit4 of @Test different, his duties can not declare any property is a single, expanded testing will provide additional test by the Jupiter
@ParameterizedTest Representation is a parametric test, there will be described in detail below
@RepeatedTest Representation can be performed repeatedly, there will be described in detail below
@DisplayName Set to display the name of the test class or test method
@BeforeEach Represents performed before each unit test
@AfterEach Represents performed after each unit test
@BeforeAll Before that the implementation of all the unit tests
@AfterAll After that the implementation of all the unit tests
@Tag 表示单元测试类别,类似于JUnit4中的@Categories
@Disabled 表示测试类或测试方法不执行,类似于JUnit4中的@Ignore
@Timeout 表示测试方法运行如果超过了指定时间将会返回错误
@ExtendWith 为测试类或测试方法提供扩展类引用

新的特性

更强大的断言

JUnit5使用了新的断言类:org.junit.jupiter.api.Assertions。相比之前的Assert断言类多了许多新的功能,并且大量方法支持Java8的Lambda表达式。

以下为两个与JUnit4不太一样的断言方式:

1. 异常断言

在JUnit4时期,想要测试方法的异常情况时,需要用 @Rule注解的ExpectedException变量还是比较麻烦的。而JUnit5提供了一种新的断言方式 Assertions.assertThrows() ,配合函数式编程就可以进行使用。

@Test
@DisplayName("异常测试")
public void exceptionTest() {
    ArithmeticException exception = Assertions.assertThrows(
           //扔出断言异常
            ArithmeticException.class, () -> System.out.println(1 % 0));

}

2. 超时断言

Junit5还提供了Assertions.assertTimeout() 为测试方法设置了超时时间

@Test
@DisplayName("超时测试")
public void timeoutTest() {
    //如果测试方法时间超过1s将会异常
    Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}

参数化测试

参数化测试是JUnit5很重要的一个新特性,也是我认为JUnit5最惊艳到我的一个功能。它使得用不同的参数多次运行测试成为了可能,也为我们的单元测试带来许多便利。

基础用法

利用 @ValueSource 等注解,指定入参,我们将可以使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。

@ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型

@NullSource: 表示为参数化测试提供一个null的入参

@EnumSource: 表示为参数化测试提供一个枚举入参

@ParameterizedTest
@ValueSource(strings = {"one", "two", "three"})
@DisplayName("参数化测试1")
public void parameterizedTest1(String string) {
    System.out.println(string);
    Assertions.assertTrue(StringUtils.isNotBlank(string));
}

进阶用法

当然如果参数化测试仅仅只能做到指定普通的入参还达不到让我觉得惊艳的地步。让我真正感到他的强大之处的地方在于他可以支持外部的各类入参。如:CSV,YML,JSON 文件甚至方法的返回值也可以作为入参。只需要去实现ArgumentsProvider接口,任何外部文件都可以作为它的入参。

@CsvFileSource:表示读取指定CSV文件内容作为参数化测试入参

@MethodSource:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)

/**
 * csv文件内容:
 * shawn,24
 * uzi,50
 */
@ParameterizedTest
@CsvFileSource(resources = "/test.csv")  //指定csv文件位置
@DisplayName("参数化测试-csv文件")
public void parameterizedTest2(String name, Integer age) {
    System.out.println("name:" + name + ",age:" + age);
    Assertions.assertNotNull(name);
    Assertions.assertNotNull(age);
}

@ParameterizedTest
@MethodSource("method")    //指定方法名
@DisplayName("方法来源参数")
public void testWithExplicitLocalMethodSource(String name) {
    System.out.println(name);
    Assertions.assertNotNull(name);
}

static Stream<String> method() {
    return Stream.of("apple", "banana");
}

为什么要用参数化测试?

介绍完参数化测试,可能会觉得这个功能确实强大但是似乎应用场景并不多呀,毕竟我们写的大部分单元测试都不会去复用。其实不然,从外部文件读取入参有一个很大的好处。没错,就是解耦,使得测试逻辑与测试参数解耦,未来如果测试的参数改变了,我们不需要去修改测试代码,只需要修改对应的文件。让我们的测试逻辑不会被大量构造测试参数的代码干扰,能更专注于写好测试逻辑。

目前我们公司也有封装自己的 @FileSource注解,用于从外部Json或Yaml文件中读取入参。单元测试代码的逻辑会非常清晰。相比于之前要自己用代码mock许多测试参数,使用这种方式可以让测试代码逻辑更加清晰。

@DisplayName("创建咨询来源:成功创建")
@ParameterizedTest
@FileSource(resources = "testData/consultconfig_app_service.yaml")
public void testSaveConsultSource_SUCCESS(ConsultConfigAppServiceDTO dto) {
    //获取入参
    ConsultSourceSaveDTO saveDTO = dto.getSaveConsultSourceSuccess();
    //调用测试方法
    ConsultSourceBO consultSourceBO = consultConfigAppService.saveConsultSource(saveDTO);
    //验证
    Assertions.assertEquals("testConsultSourceCmd", consultSourceBO.getConsultChannelName());
}

内嵌单元测试

JUnit5提供了嵌套单元测试用于更好表示各个单元测试类之间的关系。平时我们写单元测试时一般都是一个类对应一个单元测试类。不过有些互相之间有业务关系的类,他们的单元测试完全是可以写在一起,使用内嵌的方式表示,减少测试类的数量防止类爆炸。

JUnit 5 provides @Nested annotations can be logically grouped as a test class class static inner member

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("第三层-内嵌单元测试");
            }
        }
    }

}

Repeat the test

JUnit5 to provide @RepeatedTest notes, allow a unit test execution times. In fact, I do not quite understand why you want to run a unit test many times. Currently I understand that unit testing is needed because there may be repeatedly performed in nature, and many times more to run unit tests to ensure accuracy of the test, to prevent some randomness.

@RepeatedTest(10) //表示重复执行10次
@DisplayName("重复测试")
public void testRepeated() {
    Assertions.assertTrue(1 == 1);
}

dynamics test

JUnit5 allows us to dynamically create unit tests by @TestFactory notes, unit tests are generated at run time. Note that @TestFactory modified method itself is not a unit test, but he is responsible for generating a unit test. We only need to return
DynamicTest iterator even stream to generate a different unit tests.

(Dynamic test scenarios I feel less, if you have thought, then, hope to add)

@TestFactory
@DisplayName("动态测试")
Iterator<DynamicTest> dynamicTests() {
    return Arrays.asList(
            dynamicTest("第一个动态测试", () -> assertTrue(true)),
            dynamicTest("第二个动态测试", () -> assertEquals(4, 2 * 2))
    ).iterator();
}

Epilogue

These are the basic introduction to the JUnit5. Unit testing is the software development a very important part of a good unit tests so that the quality system maintainability greatly increased. The JUnit5 offers many new features to help us to write unit tests, if your project is still in use JUnit4, may wish to try JUnit5, might be able to open the door to a new world of it?

Reference material

JUnit 5 User Guide

Junit5 new features how much you used?

Released four original articles · won praise 0 · Views 134

Guess you like

Origin blog.csdn.net/glass36/article/details/104731371