springboot unit test

Keywords: Java Junit SpringBoot Database Spring

3.springboot Unit Test
Because the company needs 80% unit test coverage, write unit test cases.Multimodal projects often make calls to other services and avoid database operations affecting the database, so all operations are mock ed out, which is the result of a simulated call.Test case writing is available for reference https://www.journaldev.com/21... , the above description is also more detailed, but the following is some of the problems you have encountered.
3.1 Profile

  <properties>
        
        <testng.version>6.14.3</testng.version>
        <junit4.version>4.12</junit4.version>
        <mockito-core.version>2.19.0</mockito-core.version>
        <powermock.version>2.0.0-beta.5</powermock.version>
    </properties>

<!-- test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- TestNG -->
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>${testng.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- JUnit 4 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit4.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- Mockito 2 -->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>${mockito-core.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- PowerMock TestNG Module -->
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-module-testng</artifactId>
            <version>${powermock.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- PowerMock JUnit 4.4+ Module -->
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-module-junit4</artifactId>
            <version>${powermock.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- PowerMock Mockito2 API -->
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-api-mockito2</artifactId>
            <version>${powermock.version}</version>
            <scope>test</scope>
        </dependency>

Can be referred to as https://www.journaldev.com/21...

These three dependencies are required to support static class mocks, and there is a version issue. Previously <powermock.version>2.0.0-beta.5</powermock.version>versions <powermock.version>1.7.5</powermock.version>encountered similar issues

java.lang.NoSuchMethodError: org.mockito.internal.handler.MockHandlerFactory.createMockHandler(Lorg/mockito/mock/MockCreationSettings;)Lorg/mockito/internal/InternalMockHandler;

Error, modified version was later resolved.

3.2. No static method calls


The serverImpl class to be tested uses @InjectMocks, while all other dependent classes are removed with @Mock, which allows you to simulate the implementation of dependent classes rather than invoke the implementation of dependent classes, such as problems caused by invoking database operations.Class is annotated with @RunWith(SpringRunner.class).

For testing code, paramUtil.getParamByParamName(PRODUCT_CODE) and openApiService.userQueryInfoById(anyString()) are used in withLearningService.getGift(passId, code, token, ip), so we need to simulate its return results using the two methods used.

when(paramUtil.getParamByParamName(PRODUCT_CODE)).thenReturn(productCode);
when(openApiService.userQueryInfoById(anyString())).thenReturn(intevee);
withLearningService.getGift(passId, code, token, ip);

when is used to simulate the return of paramUtil, you can also use any() to simulate objects, anyString() to string, and anyInt(),anyLong() to simulate other types of parameters.You can walk through each branch of your judgment during the test.
If the class is annotated with @SpringBootTest, the project will start the springboot project to load various configurations, which will be much slower. It is recommended that you mock away directly without starting the springboot environment.

3.3 Require static method
Use on Class
@RunWith(PowerMockRunner.class)
@PrepareForTest({ HttpsUtils.class, RedisUtil.class, SpringContextUtil.class,AESUtil.class,KeyGenerator.class })
Two annotations requiring classes that use static methods to be added to the @PrepareForTest parameter.General static method calls are used directly:

    PowerMockito.mockStatic(RedisUtil.class);
        PowerMockito.when(RedisUtil.get(anyString())).thenReturn("0");

But such a call would produce something similar:

Error, RedisUtil is our own encapsulated RedisUtil operation tool class. Look at the RedisUtil.get method and see the context context ApplicationContext applicationContext that needs to be injected into spring.So you need to add this context

   PowerMockito.mockStatic(SpringContextUtil.class);
        PowerMockito.when(SpringContextUtil.getApplicationContext()).thenReturn(new XmlWebApplicationContext());
        PowerMockito.mockStatic(RedisUtil.class);
        PowerMockito.when(RedisUtil.get(anyString())).thenReturn("0");

This can solve the problem, I have encountered an encryption and decryption class error during the process of writing, the tool class code is as follows

    if (key == null || content == null || content.isEmpty()) {
            return null;
        }
        String msg = null;

        byte[] byteRresult = new byte[content.length() / 2];

        for (int i = 0; i < content.length() / 2; i++) {
            int high = Integer.parseInt(content.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(content.substring(i * 2 + 1, i * 2 + 2), 16);
            byteRresult[i] = (byte) (high * 16 + low);
        }

        try {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            kgen.init(128);
            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            byte[] result = cipher.doFinal(byteRresult);
            return new String(result);

The mock of the test class is written as follows:

        PowerMockito.mockStatic(AESUtil.class);
        PowerMockito.when(AESUtil.decrypt(anyString(), anyString())).thenReturn("11");

Write the error in this way, and complete the test case writing in the following way.

        PowerMockito.mockStatic(KeyGenerator.class);
        KeyGenerator keyg = Mockito.mock(KeyGenerator.class);
        PowerMockito.when(KeyGenerator.getInstance(anyString())).thenReturn(keyg);
        PowerMockito.mockStatic(AESUtil.class);
        PowerMockito.when(AESUtil.decrypt(anyString(), anyString())).thenReturn("11");

With KeyGenerator keyg = Mockito.mock(KeyGenerator.class); this code allows you to basically write test classes for all your code by mocking an object like this.The best tests here are for the server layer. Why not also test the controller layer? In fact, the main purpose of unit test is to run basic logic. The controller layer test can be handed over to test to some extent. Unit test is hard to write and painful to cover every branch of judgment.Writing tests can, to a certain extent, cause code errors that allow you to refactor your code.

Posted by frao_0 on Sat, 27 Apr 2019 02:42:35 -0700