[JUnit] JUnit 5 Foundation 3 - dependency injection, assume, enable / disable testing and nested testing

Keywords: Java Junit unit testing

[JUnit] JUnit 5 Foundation 3 - dependency injection, assume, enable / disable testing and nested testing

Dependency Injection

Before Junit 5, Junit did not support passing parameters in test constructors or methods very well, but Junit 5 allows metadata to be passed into constructors and methods, so dependency injection can also be used in test methods / constructors.

For example, the built-in TestInfo object in Junit5 can be obtained at runtime through DI:

package com.example;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;

@DisplayName("App Test with spaces")
public class AppTest
{
    @BeforeAll
    public static void beforeAll(TestInfo testInfo){
        System.out.println("********* execute beforeAll() *********");
        System.out.println("Display name - " + testInfo.getDisplayName());
        System.out.println("Test Class   - " + testInfo.getTestClass());
        System.out.println("Test Method  - " + testInfo.getTestMethod());
        System.out.println("****************************************");
    }

    public AppTest(TestInfo testInfo) {
        System.out.println("********* execute constructor **********");
        System.out.println("Display name - " + testInfo.getDisplayName());
        System.out.println("Test Class   - " + testInfo.getTestClass());
        System.out.println("Test Method  - " + testInfo.getTestMethod());
        System.out.println("****************************************");
    }

    @BeforeEach
    public void BeforeEach(TestInfo testInfo) {
        System.out.println("********* execute beforeEach() *********");
        System.out.println("Display name - " + testInfo.getDisplayName());
        System.out.println("Test Class   - " + testInfo.getTestClass());
        System.out.println("Test Method  - " + testInfo.getTestMethod());
        System.out.println("****************************************");
    }

    @Test
    public void testOne(TestInfo testInfo) {
        System.out.println("********* execute testOne() ***********");
        System.out.println("Display name - " + testInfo.getDisplayName());
        System.out.println("Test Class   - " + testInfo.getTestClass());
        System.out.println("Test Method  - " + testInfo.getTestMethod());
        System.out.println("****************************************");
    }
}

The operation result is:

Through dependency injection, TestInfo can judge different display names and methods at run time, rather than manually declared by the user.

Assume

In addition to assertions, Junit5 can also judge values through assumptions.

assumeTrue & assumeFalse

package com.example;

import static org.junit.jupiter.api.Assumptions.assumeFalse;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

import org.junit.jupiter.api.Test;

public class AppTest
{
    @Test
    void testAssumeTrue() {
        int num1 = 1, num2 = 1;
        assumeTrue(num1 == num2);
    }

    @Test
    void testAssumeTrueFail() {
        int num1 = 1, num2 = 1;
        assumeTrue(num1 != num2, "assumption fail");
    }

    @Test
    void testAssumeFalse() {
        int num1 = 1, num2 = 1;
        assumeFalse(num1 != num2);
    }
}

Operation results:

assumeThat

package com.example;

import static org.junit.jupiter.api.Assumptions.assumingThat;

import org.junit.jupiter.api.Test;

public class AppTest
{
    @Test
    void testAssumeThat1() {
        int val1 = 1, val2 = 0;
        assumingThat(val1 == val2, () -> {
            System.out.println("working on this part");
        });
    }

    @Test
    void testAssumeThat2() {
        int val1 = 1, val2 = 0;
        assumingThat(val1 != val2, () -> {
            System.out.println("will skip this part");
        });

        System.out.println("still print this line");
    }
}

Operation results:

Application scenario of assume

At first glance, there seems to be no difference between assume and assert, but in assumeTrue and assumeFalse, there is not one next to the testAssumeTrueFail() function ❌, But a ❔, This is because Junit5 skips this method and goes directly to the next test method. If assert is used, Junit5 will judge that the test method fails and return it directly ❌.

In the assumpingthat method, the difference is more obvious. Although the judgment of assumpingthat in testAssumeThat2() is wrong, the test method does not terminate, but skips the contents of assumpingthat, directly jumps to the next line of code and outputs.

This also makes the application scenario of assume clear: it can be used for testing in different environments, such as:

package com.example;

import static org.junit.jupiter.api.Assumptions.assumingThat;

import org.junit.jupiter.api.Test;

public class AppTest
{
    @Test
    void testAssumeTha2() {
        System.setProperty("ENV", "DEV");
        assumingThat("DEV".equals(System.getProperty("ENV")), () -> {
            // Test with assert
        });

        System.out.println("Run tests that you want to run in all environments");
    }
}

Enable and disable tests

Disable test

@The Disabled annotation can be used to disable testing:

package com.example;

import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

@Disabled
public class AppTest
{
    @Test
    void test1() {
        assertTrue(1 == 2);
    }

    @Test
    void test2() {
        assertTrue(1 == '1');
    }
}

The operation results are as follows:

As can be seen from the test interface on the left, Junit directly skipped all tests, neither indicating success nor failure.

Similarly, @ Disabled can also be used on the method. When it is mounted on the method, it will skip the current test method and proceed to the next test.

Enable / disable OS

This method can make the test method not run on a specific OS, such as:

package com.example;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.OS;

public class AppTest
{
    @Test
    void test1() {
        System.out.println("Linux/MacOS/Windows");
    }

    @DisabledOnOs(OS.MAC)
    @Test
    void test2() {
        System.out.println("Linux/Windows");
    }

    @DisabledOnOs(OS.WINDOWS)
    @Test
    void test3() {
        System.out.println("Linux/MacOS");
    }
}

My system is Windows, so test3() will be disabled:

There is @ DisabledOnOs() and @ EnabledOnOs().

Turn JRE on / off

This is similar to the configuration of enabling and disabling OS. It allows the test method to run in the specified JRE environment.

System attribute conditions

This refers to the SystemProperty variable, and the corresponding annotations are @ DisabledIfSystemProperty and @ EnabledIfSystemProperty.

package com.example;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;

public class AppTest
{
    @DisabledIfSystemProperty(named="os.name", matches="Windows 10")
    @Test
    void test2() {
        System.out.println("not windows 10");
    }
}

Operation results:

Environmental variable conditions

Similar to the usage of system attribute conditions, its annotations are @ DisabledIfEnvironmentVariable and @ enabledifienvironmentvariable.

Nested test

Nested testing, that is, nested testing using anonymous classes.

package com.example;

import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

public class AppTest
{
    @Test
    void test() {
        System.out.println("outer");
    }

    @Nested
    class NestedClass {
        @Test
        void test2() {
            System.out.println("nested inner");
        }
    }
}

The operation results are as follows:

Posted by LarsLai on Thu, 07 Oct 2021 02:06:13 -0700