Junit5 usage example

Posted by bulldorc on Mon, 17 Jan 2022 08:57:46 +0100

1, Create unit test service

Right click Service - > New - > other - > JUnit test case - > next, select the method to create the unit test, or click finish directly
Relevant dependencies are attached at the end of the article

2, Sample code

// Get dev environment configuration file
@ActiveProfiles("dev")
// Specify startup class
@SpringBootTest(classes = ProjectApplication.class)
// If you want to use the Spring test framework function (for example, @ MockBean) in your test, you must use @ ExtendWith(SpringExtension.class).
// It replaces the deprecated JUnit4@RunWith(SpringJUnit4ClassRunner.class)
@ExtendWith(SpringExtension.class)
public class AppApplicationManagerServiceTest {

 	// You need to add @ SpyBean or @ MockBean annotation to return the value of the template method
    // @SpyBean annotation will call the real method if there is no simulated method return
    @SpyBean
    private AppApplicationManagerService appApplicationManagerService;
    
    // @The MockBean annotation returns null if no mock method returns
    @MockBean
    private CommonService commonService;

    @BeforeEach // Each test method is executed once before
    public void setUp() {
        when(commonService.getOsreIdByCode(Mockito.anyString())).thenReturn(107);
    }

    // Final result display name
    @DisplayName("According to tenant code+Application code to obtain the list of application administrators")
    // Parametric testing can be used in combination with @ CsvSource, @ EnumSource, @ MethodSource and other annotations
    @ParameterizedTest
    // The parameters are separated by commas. Taking this as an example, this test method will be executed three times, and the parameters correspond to each other
    @CsvSource({ "dev002, SP-CI, hrdashboard", "dev002, CDPLIFE, hrdashboard", "dev002, third, hrdashboard" })
    public void testGetApplicationManager(String tenantCode, String appCode, String sysCode) {
          // If approlemapper. Is called Selectbyexample (any()) method. If the parameter is an object of any type, null will be returned
           // This method can be used to simulate the return value for database related operations or calling other microservice operations
           when(appRoleMapper.selectByExample(any())).thenReturn(null);
        // Execution test method
        List<String> resList = appApplicationManagerService.getApplicationManager(tenantCode, appCode, sysCode);
        if ("SP-CI".equals(appCode)) {
            boolean judge = DataUtils.isEmptyList(resList);
            // Assertion, each test method assertion is preferably greater than or equal to 1
            assertTrue(judge);
        }
    }
}

3, Annotation

1,@SpyBean,@MockBean,@Autowired

  • If you do not need to simulate the return value of the method, you can use @ Autowired or @ SpyBean

  • If you need to call both real methods and simulated method return values, you can only use @ SpyBean

  • If you only need to simulate the return value of the method, you can use @ MockBean or @ SpyBean

2,@ExtendWith(SpringExtension.class)

If you want to use the Spring test framework function (for example, @ MockBean) in your test, you must use @ ExtendWith(SpringExtension.class). It replaces the deprecated JUnit4@RunWith(SpringJUnit4ClassRunner.class)

3,@BeforeEach,@BeforeAll,@AfterAll,@AfterEach

  • @Before each: each test method is executed before execution

  • @After each: each test method will be executed at the end of execution

  • @Before all: all test methods will be executed before execution

  • @After all test methods are executed

4,@DisplayName()

The name of the method displayed in the run result

5,@ParameterizedTest,@RepeatedTest()

For parametric testing, you need to replace @ Test and add at least one source. The source will provide parameters for each call, and then use the parameters in the Test method. as

@ParameterizedTest
@CsvSource({ "dev002, SP-CI, hrdashboard", "dev002, CDPLIFE, hrdashboard", "dev002, third, hrdashboard" })

This means that the method with these two annotations will be executed three times, and three parameters will be passed in each time

@RepeatedTest(n): repeatability test, i.e. n times

6,@ValueSource,@EnumSource,@MethodSource,@CsvSource,@CsvFileSource,@ArgumentsSource

These annotations are used in combination with @ ParameterizedTest to specify the parameter source.

// Execute the method three times. The type also supports float, String, long and other types
@ValueSource(ints = { 1, 2, 3 })
// @EnumSource provides a convenient way to use Enum constants. This comment provides an optional name parameter that allows you to specify which constant should be used. If omitted, all constants will be used, as shown in the following example.
@EnumSource(TimeUnit.class)
// @MethodSource allows you to reference one or more methods of a test class or an external class. Such a method must return a stream, iteratable, iterator, or an array of parameters. In addition, this method cannot accept any parameters. The factory method in the test class must be static, unless the test class is annotated with @ TestInstance(Lifecycle.PER_CLASS);
@MethodSource("roleAndMenuParam")
void testSavePlatformRoleAndAuthMenu(PlatformAdminAuthMenuForm platformAdminAuthMenuForm) { 
}
static List<PlatformAdminAuthMenuForm> roleAndMenuParam() {    
}
// Parameters are separated by commas. Taking this as an example, the test method will be executed three times and three parameters will be passed in
@CsvSource({ "dev002, SP-CI, hrdashboard", "dev002, CDPLIFE, hrdashboard", "dev002, third, hrdashboard" })
// @CsvFileSource allows the use of CSV files in the classpath. Each line in the CSV file invokes a parametric test.
@CsvFileSource(resources = "/two-column.csv", numLinesToSkip = 1)

@ArgumentsSource can be used to specify a custom and reusable ArgumentsProvider.

7,@Disabled

The method with this annotation will not be executed when running

4, Assert

Assertions suggest at least one in each test method

Junit5's assertions support Lambda expressions

Some common assertions are listed below:

  • Assert equals: asserts that the expected and actual are equal

  • assertNotNull: the result is not null

  • assertThrows: verify the exception. If no exception is thrown or the exception thrown is inconsistent with the expected exception, the assertion will fail

  • assertAll: grouping assertions, which can be composed of multiple assertions. As long as one of them fails, it will not continue to be executed

More assertions are not listed

5, Simulation method return value

When writing a unit test, you sometimes need to let the method return the result you want. In this case, you need to use mock to simulate the return value of the method

The principle of mockito simulation is to create an anonymous subclass that inherits the original class through reflection, and use the created subclass to execute instead of the original class. Because it is inheritance, static methods, final methods and private methods cannot be overridden through subclasses

Analog return value

// Original method
appRoleMapper.selectByExample(AppRoleExample example);
// Analog return value
when(appRoleMapper.selectByExample(any())).thenReturn(null);

The function of this method is to execute approlemapper Selectbyexample (approleexample example) method. If the parameter is of any type, it will not call the database and will directly return null

doNothing

Some methods have no return value, such as sending messages. If you don't need to call these methods, you can use doNothing method, so you won't execute this method when you encounter this method. An example is as follows:

// Original method
commonService.verifyFieldRequired(Object obj, String jsonPaths){}
// doNothing using
Mockito.doNothing().when(commonService).verifyFieldRequired(Mockito.any(), Mockito.anyString());

Mockito parameter

When simulating a method, you need parameters. You can specify any() or anyString() to specify the parameter type. Any represents any type of object, anyString() represents any String type parameter, and other types:

any(Class type): specific classes can be passed in. If any(AppRole.class) represents any role object

anyBoolean(): data of any boolean type

anyListOf(Class clazz): represents a list of a type

anyObject(): any object

More methods are not listed one by one

Create simulation objects

Test data sometimes need more, and it is cumbersome to create one by one. You can use EasyRandom to create random objects

EasyRandom easyRandom = new EasyRandom();
// Generate a single object
AppMenu menu = easyRandom.nextObject(AppMenu.class);
// Generate list
List<AppMenu> menuList = easyRandom.objects(AppMenu.class, 10).collect(Collectors.toList());
// Generate other types of data
boolean boo = easyRandom.nextBoolean();

If no parameters are set, the randomly generated data is chaotic. You can add a configuration to make the generated data more meet our requirements. The use method is as follows

EasyRandomParameters param = new EasyRandomParameters();
// Set string length range
param.setStringLengthRange(new Range(5,10));
// When creating an object, the configuration parameters are passed in
EasyRandom easyRandom = new EasyRandom(param);
for (int i = 0; i < 5; i++) {
    String str = easyRandom.nextObject(String.class);
    System.out.println(str);
}
// result
tniOAot
nzWvk
QtWruzGVy
IdgFUY
NdkFYkk

See the official documentation for more configurations: https://github.com/j-easy/easy-random/wiki/Randomization-parameters

Simulate the return value of static method and private method

As mentioned earlier, mockito is simulated by creating subclasses of the original class through reflection. This method cannot simulate static methods, private methods and final methods. At this time, JMockit can be used to implement it

JMockit Chinese website: http://jmockit.cn/index.htm

JMock usage method 1:

class LogTestServiceTest {

    @Autowired
    private static LogTestService logTestService = new LogTestService();

    @Test
    void testTest() throws Exception {
        new Expectations(DataUtils.class, LogTestService.class) {
            {
                // mock static method
                DataUtils.isEmptyList(Mockito.anyList());
                result = false;
                // mock final method
                logTestService.finalTest(Mockito.anyString());
                result = "mock final";
                // Private methods and native methods cannot be mock in this way
            }
        };
        logTestService.test();
    }
}

JMock application method 2:

class LogTestServiceTest {

    @Autowired
    private static LogTestService logTestService = new LogTestService();
    
    // Write an inner class to inherit MockUp
    public static class LogTestServiceTestMockUp extends MockUp<LogTestService> {
        @Mock
        private String privateTest(String str) {
            return "mock private method";
        }
        @Mock
        final String finalTest(String str) {
            return "mock final method";
        }
    }
    
    public static class DataUtilsMockUp extends MockUp<DataUtils> {
        @Mock
        public static boolean isEmptyList(List list) {
            return false;
        }
    }

    @Test
    void testTest() throws Exception {
        new LogTestServiceTestMockUp();
        new DataUtilsMockUp();
        logTestService.test();
    }
}

6, Project dependency

<!-- mockito -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.11.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-all</artifactId>
    <version>1.10.19</version>
    <scope>test</scope>
</dependency>
<!-- junit5 -->
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-commons</artifactId>
    <version>1.8.2</version><!--$NO-MVN-MAN-VER$-->
</dependency>
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-launcher</artifactId>
    <version>1.8.2</version><!--$NO-MVN-MAN-VER$-->
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-commons</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!-- JMock -->
<dependency>
    <groupId>org.jmockit</groupId>
    <artifactId>jmockit</artifactId>
    <version>1.36</version>
    <scope>test</scope>
</dependency>

Topics: Java Junit Spring unit testing