Welcome to my GitHub

Github.com/zq2599/blog…

Content: all original article classification summary and supporting source code, involving Java, Docker, Kubernetes, DevOPS, etc.;

About the JUnit5 learning series

JUnit5 learning is a series of eight articles designed to improve unit testing skills in a SpringBoot environment. The links are as follows:

  1. Basic operation
  2. Assumptions class
  3. Assertions class
  4. Conditionality
  5. Tags and custom annotations
  6. The basis for Parameterized Tests
  7. Parameterized Tests are advanced
  8. Comprehensive Progression (Final)

This paper gives an overview of

This article is the first part of the JUnit5 Learning series. It will learn the basic functions of JUnit5 under the Framework of SpringBoot. The whole chapter is as follows:

  1. JUnit5 profile
  2. SpringBoot’s dependency on JUnit5
  3. Introduction to Common Annotations
  4. Version 5 deprecated annotations are introduced
  5. Enter the actual combat, first introduce the version and environment information
  6. Create the parent project for the JUnit5 learning series source code
  7. Create subprojects and code common annotations to experience

About JUnit5

  1. JUnit is a commonly used Java unit testing framework. The latest version of JUnit is as follows:

JUnit5 can be divided into three layers: the Framework, the Engine and the Platform. 3. JUnit5 is officially defined as consisting of three parts: Platform, Jupiter and Vintage, with the following functions; 4. Platform: Located at the bottom layer of the architecture, it is the basic Platform for unit testing on the JVM. It also interconnects with various ides (such as IDEA and Eclipse), and also interconnects with the engine layer and defines the API for the engine layer interconnection. Jupiter: located in the engine layer, supports version 5 programming model, extension model; 6. Vintage: located in the engine layer, used to execute low version test cases;

  • The whole Junit Platform is open and can be accessed by various testing frameworks through the engine API.

SpringBoot’s dependency on JUnit5

  1. Here we use SpringBoot version 2.3.4.RELEASE and rely on JUnit5 in the project pom.xml as follows:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
Copy the code
  1. In the red box below, you can see that JUnit5 jars are indirectly dependent on spring-boot-starter-test:

RunWith annotations

  1. When using JUnit4, we used to write unit test classes like this:
@RunWith(SpringRunner.class)
@SpringBootTest
public class XXXTest {
Copy the code
  1. For the RunWith annotation above, the official JUnit5 documentation, shown in the red box below, has been replaced by ExtendWith:

3. Let’s take a look at the annotation of SpringBootTest as shown below, which includes ExtendWith:

4. To sum up, when SpringBoot+JUnit5, RunWith annotations are no longer needed. Normally, only SpringBootTest annotations can be used.

Common JUnit5 annotations (SpringBoot environment)

@repeatedTest @parameterizedTest @TestFactory @repeatedTest @parameterizedTest @repeatedTest @parameterizedTest @TestFactory

  1. ExtendWith: This is used to replace the RunWith annotation in older versions, but no additional configuration is required in the SpringBoot environment unless specifically required, as it is already present in SpringBootTest;
  2. Test: The Test method is modified by this annotation;
  3. BeforeAll: methods modified by this annotation must be static, executed BeforeAll test methods, inherited by subclasses, replacing earlier versions of BeforeClass;
  4. AfterAll: Methods modified by this annotation must be static, will not be executed until all test methods have been executed, and will be inherited by subclasses instead of earlier versions of AfterClass;
  5. BeforeEach: Methods decorated with this annotation are executed once Before each test method execution and are inherited by subclasses instead of the lower version of Before;
  6. AfterEach: methods embellished with this annotation are executed once AfterEach test method execution and are inherited by subclasses instead of the lower version of Before;
  7. DisplayName: DisplayName of the test method, displayed in the test framework, supporting emoji;
  8. Timeout: specifies the Timeout period in which a modified method will fail the test if it times out.
  9. Disabled: indicates a test method that will not be executed.

Deprecated annotations for version 5

The following comments were used before version 5 and are now deprecated:

Discarded notes A new successor
Before BeforeEach
After AfterEach
BeforeClass BeforeAll
AfterClass AfterAll
Category Tag
RunWith ExtendWith
Rule ExtendWith
ClassRule RegisterExtension

Version and environment information

The entire series of coding and execution is carried out in the following environment for your reference:

  1. Hardware configuration: CPU I5-8400, memory 32 GB, hard disk 128 GB SSD + 500 GB HDD
  2. Operating system: Windows10 home Chinese version
  3. IDEA: 2020.2.2 (Ultimate Edition)
  4. The JDK: 1.8.0 comes with _181
  5. SpringBoot: 2.3.4. RELEASE
  6. JUnit Jupiter: 5.6.2

Next, start the actual combat, let’s build a good SpringBoot project;

About the lombok

To simplify the code, lombok is used in the project. Please install lombok plug-in in IDEA.

Download the source code

  1. If you don’t want to code, you can download all the source code at GitHub, with the address and link information listed in the following table (github.com/zq2599/blog…
The name of the link note
Project home page Github.com/zq2599/blog… The project’s home page on GitHub
Git repository address (HTTPS) Github.com/zq2599/blog… The project source warehouse address, HTTPS protocol
Git repository address (SSH) [email protected]:zq2599/blog_demos.git The project source warehouse address, SSH protocol
  1. The git project has multiple folders, and the application of this chapter is in the junitPractice folder, as shown in the red box below:

  1. Junitpractice is a parent-child project. This code is in the junit5experience sub-project, as shown below:

Create the Maven parent project

  1. In order to facilitate the management of the entire series of source code, the establishment of a maven project named junitPractice, all subsequent actual source code as junitPractice sub-project;
  2. Junitpractice pom.xml is the parent project of SpringBoot 2.3.4.RELEASE:

      
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <modules>
        <module>simplebean</module>
        <! -- <module>testenvironment</module> -->
    </modules>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4. RELEASE</version>
        <relativePath/> <! -- lookup parent from repository -->
    </parent>
    <groupId>com.bolingcavalry</groupId>
    <artifactId>junitpractice</artifactId>
    <version>1.0 the SNAPSHOT</version>
    <packaging>pom</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.16.16</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>
Copy the code

This source code project

Next we prepare a simple SpringBoot project for unit testing, the project has a service and controller layer, including some simple interfaces and classes;

  1. Create a subproject named junit5Experience, pop.xml as follows, note that the unit test relies on spring-boot-starter-test:

      
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.bolingcavalry</groupId>
        <artifactId>junitpractice</artifactId>
        <version>1.0 the SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <groupId>com.bolingcavalry</groupId>
    <artifactId>junit5experience</artifactId>
    <version>0.0.1 - the SNAPSHOT</version>
    <name>junit5experience</name>
    <description>Demo project for simplebean in Spring Boot junit5</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
Copy the code
  1. Write some of the simplest business code, starting with the interface helloService.java for the Service layer:
package com.bolingcavalry.junit5experience.service;

public interface HelloService {
    String hello(String name);
    int increase(int value);
    /** * This method will wait 1 second and return true, which simulates a time-consuming remote call *@return* /
    boolean remoteRequest(a);
}
Copy the code
  1. The hello and increase methods return String and int, respectively. RemoteRequest deliberately sleeps for a second to test the effect of the Timeout annotation:
package com.bolingcavalry.junit5experience.service.impl;

import com.bolingcavalry.junit5experience.service.HelloService;
import org.springframework.stereotype.Service;

@Service()
public class HelloServiceImpl implements HelloService {
    @Override
    public String hello(String name) {
        return "Hello " + name;
    }

    @Override
    public int increase(int value) {
        return value + 1;
    }

    @Override
    public boolean remoteRequest(a) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException interruptedException) {
            interruptedException.printStackTrace();
        }

        return true; }}Copy the code
  1. Add a simple controller:
package com.bolingcavalry.junit5experience.controller;

import com.bolingcavalry.junit5experience.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @Autowired
    private HelloService helloService;

    @RequestMapping(value = "/{name}", method = RequestMethod.GET)
    public String hello(@PathVariable String name){
        returnhelloService.hello(name); }}Copy the code
  1. Start the class:
package com.bolingcavalry.junit5experience;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Junit5ExperienceApplication {

    public static void main(String[] args) { SpringApplication.run(Junit5ExperienceApplication.class, args); }}Copy the code
  • That’s a typical Web project, and we’ll write unit test cases for that project together.

Write test code

  1. Add a unit test class in the red box below:

2. The contents of the test class are as follows, covering the common annotations just mentioned. Please note the annotations for each method:

package com.bolingcavalry.junit5experience.service.impl;

import com.bolingcavalry.junit5experience.service.HelloService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
@Slf4j
class HelloServiceImplTest {

    private static final String NAME = "Tom";

    @Autowired
    HelloService helloService;

    /** * is executed before all test methods are executed */
    @BeforeAll
    static void beforeAll(a) {
        log.info("execute beforeAll");
    }

    /** * is executed after all test methods are executed */
    @AfterAll
    static void afterAll(a) {
        log.info("execute afterAll");
    }

    /** * each test method is executed once */
    @BeforeEach
    void beforeEach(a) {
        log.info("execute beforeEach");
    }

    /** * each test method is executed once */
    @AfterEach
    void afterEach(a) {
        log.info("execute afterEach");
    }

    @Test
    @displayName (" Test service layer hello method ")
    void hello(a) {
        log.info("execute hello");
        assertThat(helloService.hello(NAME)).isEqualTo("Hello " + NAME);
    }

    /** * DisplayName contains emojis that can be displayed in the test framework */
    @Test
    @displayName (" Increase method \uD83D\uDE31")
    void increase(a) {
        log.info("execute increase");
        assertThat(helloService.increase(1)).isEqualByComparingTo(2);
    }

    /** * test method that will not be executed */
    @Test
    @Disabled
    void neverExecute(a) {
        log.info("execute neverExecute");
    }

    /** * call a method that takes 1 second and uses Timeout to set the Timeout to 500 milliseconds, so the use case fails the test */
    @Test
    @Timeout(unit = TimeUnit.MILLISECONDS, value = 500)
    @Disabled
    void remoteRequest(a) {
        assertThat(helloService.remoteRequest()).isEqualTo(true); }}Copy the code
  1. Next, try executing the test case by clicking the button in the red box below:

4. In the pop-up menu as shown below, click the position of the red box:

  1. The result is as follows: The Displayname value is displayed as the method name of the test result, methods that timeout are judged to have failed the test, and methods modified by the Disable annotation are marked as skipped and not executed:

  1. MVN test = MVN test = MVN test = MVN test = MVN test = MVN test

  • At this point, we have a basic understanding of JUnit5 in the SpringBoot environment, and the chapters that follow will expand into more knowledge and details and further study unit testing.

You are not alone, Xinchen original accompany all the way

  1. Java series
  2. Spring series
  3. The Docker series
  4. Kubernetes series
  5. Database + middleware series
  6. The conversation series

Welcome to pay attention to the public number: programmer Xin Chen

Wechat search “programmer Xin Chen”, I am Xin Chen, looking forward to enjoying the Java world with you…

Github.com/zq2599/blog…