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

  • In this article, JUnit5 Learning is the sixth in a series of articles on Parameterized Tests. You can perform the same test multiple times, using different parameters each time.
  • Since Parameterized Tests are powerful and contain more knowledge points than the previous chapters, they are divided into Two parts: Basic and Advanced. This part focuses on learning the basics of Parameterized Tests, including the following contents:
  1. Extreme experience;
  2. Version dependence;
  3. ValueSource data source
  4. Null, empty string data source
  5. Enumeration data source
  6. Method data source
  7. Csv data source
  8. Csv file data source

Download the source code

  1. If you don’t want to code, you can download all the source code at GitHub, with the following address and link information:
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 Parameterized sub-project, as shown below:

Extreme experience

  1. Now, let’s try out the simplest parameterized test with a minimum of steps;
  2. Create a new child project named parameterized in the parent project, JunitPractice, and pom.xml will look like this:

      
<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>parameterized</artifactId>
    <version>0.0.1 - the SNAPSHOT</version>
    <name>parameterized</name>
    <description>Demo project for parameterized expirence in Spring Boot junit</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.junit</groupId>
                <artifactId>junit-bom</artifactId>
                <version>5.7.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>

    </dependencyManagement>

    <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>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.jupiter</groupId>
                    <artifactId>junit-jupiter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</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. Create a new test class hellotest. Java in this location: junitPractice \parameterized\ SRC \test\ Java \com\ BolingCavalry \parameterized\ Service \impl.
package com.bolingcavalry.parameterized.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.assertTrue;

@SpringBootTest
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class HelloTest {

    @Order(1)
    @displayName (" multiple string inputs ")
    @ParameterizedTest
    @ValueSource(strings = { "a", "b", "c" })
    void stringsTest(String candidate) {
        log.info("stringsTest [{}]", candidate);
        assertTrue(null!=candidate);
    }
}    
Copy the code
  1. Executing the test class results in the following image:

ParameterizedTest: @valuesource: @valuesource: @valuesource: @valuesource: @valuesource: @valuesource {“a”, “b”, “c”}, each element executes once; 6. So far, we have experienced the simplest parameterized test, it can be seen that we try to make a test method executed several times, each time using different parameters, next more configuration and rules of parameterized test will be developed with actual combat code one by one, let’s experience it;

Version for

  • ParameterizedTest: springboot-2.3.4. RELEASE ParameterizedTest: springboot-2.3.4. RELEASE ParameterizedTest: Springboot-2.3.4. RELEASE ParameterizedTest: Springboot-2.3.4. RELEASE

  • ParameterizedTest: junit-Jupit-5.7.0 ParameterizedTest: Junit-Jupit-5.7.0

  • In summary, it is best to upgrade junit-Jupiter to 5.7.0 or higher if you want to use parameterized testing. If your application uses the SpringBoot framework, junit-Jupiter is indirectly dependent on spring-boot-starter-test. You need to exclude this indirect dependency and then manually rely on it to ensure that the specified version is used. Perform the following three steps in PEM.xml:
  1. DependencyManagement Add junit-BOM to the node and specify the version number:
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.junit</groupId>
      <artifactId>junit-bom</artifactId>
      <version>5.7.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
Copy the code
  1. Exclude the indirect dependencies of spring-boot-starter-test and Junit-Jupiter:
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
  <exclusions>
    <exclusion>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
    </exclusion>
  </exclusions>
</dependency>
Copy the code
  1. Add the junit-Jupiter dependency using the version number specified in dependencyManagement:
<dependency>
  <groupId>org.junit.jupiter</groupId>
  <artifactId>junit-jupiter</artifactId>
  <scope>test</scope>
</dependency>
Copy the code
  1. Update to see version 5.7.0 already used:

  • Now that the versioning issue is resolved, we are ready to learn Parameterized Tests. First, we need to know what data sources are available.

ValueSource data source

  1. ValueSource is the simplest commonly used data source, supporting arrays of the following types:
    short

    byte

    int

    long

    float

    double

    char

    boolean

    java.lang.String
    
    java.lang.Class
Copy the code
  1. Here is a demonstration of an integer array:
    @Order(2)
    @displayName (" multiple ints ")
    @ParameterizedTest
    @valuesource (ints = {1,2,3})
    void intsTest(int candidate) {
        log.info("ints [{}]", candidate);
        assertTrue(candidate<3);
    }
Copy the code
  1. As can be seen from the above code, the assertTrue fails when the input parameter is equal to 3, and the test method fails.

Null, empty string data source

  1. When using a string as an input parameter, ValueSource usually considers null.
@ValueSource(strings = { null, "a", "b", "c" })
Copy the code
  1. The @nullSource annotation can be used to replace the null element above, and this will have the same effect as above:
    @NullSource
    @ValueSource(strings = { "a", "b", "c" })
Copy the code
  1. In the red box below, null is executed as an input parameter:

4. Similar to @nullSource representing a null input parameter, @emptysource represents an empty string input parameter. The usage and execution results are shown below:

NullAndEmptySource @nullandemptysource @nullandemptysource @nullandemptysource @nullandemptysource

Enumeration Data Source

  1. EnumSource can have all or some of the values in an enumeration class as inputs to test methods.
  2. Create the enumeration class types.java for the next field use as follows, simply with three values:
public enum Types {
    SMALL,
    BIG,
    UNKNOWN
}
Copy the code
  1. JUnit knows which enumeration to use based on the input type of the test method by simply adding @enumSource:
    @Order(6)
    @displayName (" multiple enumerated input arguments ")
    @ParameterizedTest
    @EnumSource
    void enumSourceTest(Types type) {
        log.info("enumSourceTest [{}]", type);
    }
Copy the code
  1. The execution result is as follows:

5. If you do not want to execute all of the values of the enumeration, you can specify only a portion of them in the name attribute:

@EnumSource(names={"SMALL", "UNKNOWN"})
Copy the code
  1. The execution result is as follows:

7. You can also specify which values will not be executed by adding the mode attribute and setting it to EXCLUDE. (If the mode attribute is not written, the default value is INCLUDE, which was the default value in the previous example) :

@EnumSource(mode= EnumSource.Mode.EXCLUDE, names={"SMALL", "UNKNOWN"})
Copy the code
  1. The command output is as follows. SMALL and UNKNOWN are not executed:

Method Data Source (MethodSource)

  1. @methodSource can specify a method name that returns a collection of elements as an input to the test method;
  2. Start by defining a method that is normally static (otherwise @testInstance) and returns a Stream:
    static Stream<String> stringProvider(a) {
        return Stream.of("apple1"."banana1");
    }
Copy the code
  1. Then, the test method uses @methodSource and specifies the method name stringProvider:
    @Order(9)
    @displayName (" Static method returns a collection with each element in the collection as an input parameter ")
    @ParameterizedTest
    @MethodSource("stringProvider")
    void methodSourceTest(String candidate) {
        log.info("methodSourceTest [{}]", candidate);
    }
Copy the code
  1. The stringProvider method and test method methodSourceTest above are in the same class. If they are not in the same class, specify the entire package path of the static method, the class name, and the method name, as shown below, with # concatenated between the class and method names:
@Order(10)
    @displayName (" Static method returns collection, static method in another class ")
    @ParameterizedTest
    @MethodSource("com.bolingcavalry.parameterized.service.impl.Utils#getStringStream")
    void methodSourceFromOtherClassTest(String candidate) {
        log.info("methodSourceFromOtherClassTest [{}]", candidate);
    }
Copy the code
  1. If not @ MethodSource specified in the method name, JUnit finding a static method and the test method of the same name, for example, the static method methodSourceWithoutMethodNameTest will be as a test method of data sources:
    static Stream<String> methodSourceWithoutMethodNameTest(a) {
        return Stream.of("apple3"."banana3");
    }

    @Order(11)
    @displayName (" Static method returns collection, does not specify static method name, automatically matches ")
    @ParameterizedTest
    @MethodSource
    void methodSourceWithoutMethodNameTest(String candidate) {
        log.info("methodSourceWithoutMethodNameTest [{}]", candidate);
    }
Copy the code
  1. The result is as follows:

Csv data source (CsvSource)

  1. CsvSource = CsvSource = CsvSource = CsvSource = CsvSource = CsvSource = CsvSource = CsvSource = CsvSource = CsvSource
    @Order(12)
    @displayName ("CSV format multiple record entries ")
    @ParameterizedTest
    @CsvSource({ "apple1, 11", "banana1, 12", "'lemon1, lime1', 0x0A" })
    void csvSourceTest(String fruit, int rank) {
        log.info("csvSourceTest, fruit [{}], rank [{}]", fruit, rank);
    }
Copy the code
  1. The result is as follows. From the log, it can be determined that the two fields of each record match the two input parameters of the test method:

  1. CsvSource also provides a nullValues attribute that identifies the specified string as null. The following setting identifies all NIL in the CSV data as null and passes it to the test method:
    @Order(13)
    @displayName ("CSV format multiple record entry (recognize NULL)")
    @ParameterizedTest
    @CsvSource(value = { "apple2, 21", "banana2, 22", "'lemon2, lime2', 0x0A", "NIL, 3" }, nullValues = "NIL" )
    void csvSourceWillNullTokenTest(String fruit, int rank) {
        log.info("csvSourceWillNullTokenTest, fruit [{}], rank [{}]", fruit, rank);
    }
Copy the code
  1. The visible string NIL becomes null when it reaches the test method:

Csv file data source

  1. @csvSource solves the problem of test method input having multiple fields, but writing the test data as input to the source file does not seem appropriate, especially if the data is very large. This scenario is suitable for @csvfilesource. Note that the numLinesToSkip property specifies the number of rows to skip and can be used to skip the header:
    @Order(14)
    @displayName ("CSV file with multiple entries ")
    @ParameterizedTest
    @CsvFileSource(files = "src/test/resources/two-column.csv", numLinesToSkip = 1)
    void csvFileTest(String country, int reference) {
        log.info("csvSourceTest, country [{}], reference [{}]", country, reference);
    }
Copy the code
  1. Create a two-column. CSV file in SRC /test/resources/ with the following contents:
Country, reference
Sweden, 1
Poland, 2
"United States of America", 3
Copy the code
  1. The above code execution result is as follows, the code is more concise without test data:

Look forward to the advanced chapter

  • We can use various data source annotations to create more parameters for the test method. However, there are still some problems to be solved, such as more free data source customization and improved multi-field processing scheme. In the next article on Advanced, let’s experience more advanced features of parameterized 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…