This one is enough for getting started with a detailed tutorial on mock testing

1. What is a mock test

1.png

Mock testing is to use a virtual object (Mock object) to create a test method for testing in the test activity, for some more complex data/scenario that is not easy to construct or obtain .

2. Why Mock Test

Mock is to solve the problem that it is difficult to develop and test due to coupling between different units. Therefore, Mock can appear not only in unit tests , but also in integration tests and system tests .

Mock's biggest function is to help you decompose the coupling of unit tests. If your code has dependencies on another class or interface, it can help you simulate these dependencies and help you verify the behavior of the called dependencies.

3. Mock application scenarios

1. It is necessary to separate the current unit under test from its dependent modules, construct an independent test environment, and not pay attention to the dependent objects of the unit under test, but only focus on the functional logic of the unit under test.

2. The module that the unit under test depends on has not been developed A, and the unit under test needs to rely on the return value of the module for subsequent processing.

3. In the front-end and back-end projects, before the development of the back-end interface is completed, the joint debugging of the interface

4. The interface of the dependent upstream project has not been developed yet, and the joint debugging test of the interface is required

5. Objects that the unit under test depends on are difficult to simulate or have complex structures

For example: There are many abnormal conditions in the payment business, but simulating such abnormal conditions is very complicated or impossible to simulate.

4. Code example

New test project

package com.echo.mockito;
 
public class demo {
 
    //新建一个测试方法
    public int add(int a,  int b){
        return a + b;
    }
}

Build a mock test method

Select the test class, right click and select generate

2.png

click test

3.png

After clicking ok, the corresponding test method will be generated in the test directory, corresponding to the real directory

4.png

5. Parameter method description

@BeforeEach

It is used to prepare before the test. Many environment configurations or basic configurations will be built before the test, which can be set here.

@AfterEach

For post-test setup.

@Mock

Annotations can be understood as a substitute for the mock method, which does not follow the real method and simulates the behavior of the real method. When using this annotation, use the MockitoAnnotations.openMocks(this) method to make the annotation take effect.

@Spy

1. The spy object will follow the real method, but the mock object will not,

2. The parameter of the spy method is the object instance, and the parameter of the mock is the class.

@InjectMocks

Used to inject mock variables marked with @Mock into test classes.

MockitoAnnotations.openMocks(this)

Turn on the mock and test it with the above two annotations. It is generally placed in @BeforeEach and turned on before the test, so that it does not need to be turned on in every method.

Mockito.when(demo.add(1,2)).thenReturn(3): piling

The mock core can set the result of the method to be tested, so that the execution result of the real method will be ignored, and subsequent tests will be executed based on the piling result.

Mockito.when(demo.add(1,2)).thenThrow(new RuntimeException());

Used to simulate exceptions.

Assertions.assertEquals(3,demo.add(1,2)): Assertions

The primary means of testing, on which results are judged. (expected value, actual value).

6. Simple test

The setting method of piling test returns 4, but the actual execution is 3, and the test fails.

5.png

No piling test Because it is a spy method, the real method will be used, and the test will pass.

6.png

If it is a mock method, if there is no piling, there will be a default value, and the test will fail. You can try it.

7.png

7. Description of test method

See the code for detailed calls, generally as follows:

  • Piling test

  • exception test

  • real method call

package com.echo.mockito;
 
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
 
 
class demoTest {
 
     @Mock
     demo demo;
     //测试前开启mock
    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }
 
    @Test
    void add() {
        //mock 打桩,就是不管真实的方法如何执行,我们可以自行假设该方法执行的结果
        //后续的测试都是基于打桩结果来走
       // Mockito.when(demo.add(1,2)).thenReturn(4);
       // Assertions.assertEquals(3,demo.add(1,2));
        //当测试方法出现异常,测试方法  如果有try{}catch{} 则可以测试异常是否正常
        //Mockito.when(demo.add(1,1)).thenThrow(new RuntimeException());
        //调用真实的方法
        Mockito.when(demo.add(1,1)).thenCallRealMethod();
        Assertions.assertEquals(2,demo.add(1,1));
    }
 
    @AfterEach
    void after(){
        System.out.println("测试结束");
    }
}

8. Mock static method

In the previous version, it was not allowed to simulate and test static methods. If you need to test static methods, you need to replace the new mock dependencies and comment out the current dependencies, which will cause conflicts.

Static method testing should use mock's MockedStatic class to construct the test method.

  <!--   <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>4.6.1</version>
        </dependency>
-->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-inline</artifactId>
            <version>4.3.1</version>
            <scope>test</scope>
        </dependency>
package com.echo.mockito.Util;
 
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
 
import java.util.Arrays;
 
import static org.junit.jupiter.api.Assertions.*;
 
class StaticUtilsTest {
 
    @BeforeEach
    void setUp() {
    }
 
    // 有参静态方法构建
    @Test
    void range() {
        MockedStatic   demo = Mockito.mockStatic(StaticUtils.class);
        //打桩
        demo.when(()->StaticUtils.range(2,6)).thenReturn(Arrays.asList(10,11,12));
        Assertions.assertTrue(StaticUtils.range(2,6).contains(11));
    }
    // 无参静态方法构建
    @Test
    void name() {
        MockedStatic   demo = Mockito.mockStatic(StaticUtils.class);
        //打桩
        demo.when(StaticUtils::name).thenReturn("dhmw");
        Assertions.assertEquals("dhmw",StaticUtils.name());
 
    }
}

Problem: There is no problem with the execution of a single method, but when we execute all the methods on the class, we find that an error is reported.

提示static mocking is already registered in the current thread  To create a new mock, the existing static mock registration must be deregistered

This means that each method needs to have its own static mock object, which is not allowed to be shared. When executed together, the first method occupies the object, and the second method has no way to occupy it.

8.png

Solution: Close the mock object demo.close() directly after each method is executed. Equivalent to a singleton. Release it after use, and the next method can then be used.

  @Test
    void range() {
        MockedStatic   demo = Mockito.mockStatic(StaticUtils.class);
        //打桩
        demo.when(()->StaticUtils.range(2,6)).thenReturn(Arrays.asList(10,11,12));
        Assertions.assertTrue(StaticUtils.range(2,6).contains(11));
       //关闭
        demo.close();
    }
    // 无参静态方法构建
    @Test
    void name() {
        MockedStatic   demo = Mockito.mockStatic(StaticUtils.class);
        //打桩
        demo.when(StaticUtils::name).thenReturn("dhmw");
        Assertions.assertEquals("dhmw",StaticUtils.name());
        //关闭
        demo.close();
    }

9. Improve test coverage

Case: In the data statistics system, the local pusher enters the customer's name and mobile phone number, and finally builds a user object and stores it in the data table.

The business code is as follows:

package com.echo.mockito.service.impl;
 
import com.echo.mockito.dao.UserDao;
import com.echo.mockito.service.RegistrationService;
import com.echo.mockito.vo.User;
import org.springframework.beans.factory.annotation.Autowired;
 
import javax.xml.bind.ValidationException;
import java.sql.SQLException;
 
public class RegistrationServiceImpl implements RegistrationService {
     @Autowired
     UserDao userDao;
    @Override
    public User register(String name, String phone) throws Exception {
        if (name == null || name.length() == 0){
            throw new ValidationException("name 不能为空");
        }
        if (phone == null || phone.length() ==0 ){
            throw new ValidationException("phone 不能为空");
        }
        User user;
        try {
              user = userDao.save(name,phone);
        }catch (Exception e){
            throw  new Exception("SqlException thrown" + e.getMessage());
        }
        return user;
    }
}
package com.echo.mockito.dao;
 
import com.echo.mockito.vo.User;
 
public class UserDao {
 
    public User save(String name,String phnoe){
        User user = new User();
        user.setName(name);
        return user;
    }
}

To generate the corresponding test code, there are several issues to consider at this time.

1. The class to be tested is RegistrationServiceImpl, but how does the userDao in it be injected into the test class?

2. Because of the need for test coverage, it is necessary to consider that there are several situations in the test class that need to be tested.

(1): Two ifs throw two exceptions, and there are a total of 2 situations to be tested.

(2): Save the database is divided into normal save and abnormal save, a total of 2 cases of testing.

In summary, testing for these four cases must be done in order to cover all the code.

In the same way, we generate test cases . There are detailed instructions in the code, and there are test cases for each case.

package com.echo.mockito.service.impl;
 
import com.echo.mockito.dao.UserDao;
import com.echo.mockito.vo.User;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.*;
 
import javax.xml.bind.ValidationException;
 
import java.sql.SQLException;
 
import static org.junit.jupiter.api.Assertions.*;
 
class RegistrationServiceImplTest {
 
  @InjectMocks     //RegistrationServiceImpl 实例中注入@Mock标记的类,此处是注入userDao
    @Spy
    private RegistrationServiceImpl registrationService;
    @Mock
    private  UserDao userDao;
    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }
 
    @Test
    void register() throws Exception {
        // ------------------  第一种 name  异常情况   测试   start ------------------------
        String name = null;
        String phone = "1234";
        try {
          registrationService.register(name,phone);
        }catch (Exception e){
        Assertions.assertTrue(e instanceof ValidationException);
        }
 
        // ------------------  name  异常情况   测试   end  ------------------------
 
        // ------------------  第二种 phone  异常情况   测试   start  ------------------------
        name = "111";
        phone = null;
      try {
        registrationService.register(name,phone);
      }catch (Exception e){
        Assertions.assertTrue(e instanceof ValidationException);
      }
        // ------------------  phone  异常情况   测试   start  ------------------------
 
 
        // ------------------  第三种 userDao.save   正常情况   测试   start  ------------------------
      name = "111";
      phone = "111";
        //正常保存测试  打桩  走真实的方法
        Mockito.when(userDao.save(name,phone)).thenCallRealMethod();
        User user =  registrationService.register(name,phone);
        Assertions.assertEquals("111",user.getName());
        // ------------------  userDao.save   正常情况   测试   end  ------------------------
 
        // ------------------   第四种 userDao.save   异常情况   测试   start  ------------------------
      //异常保存测试  打桩   通过thenThrow 抛出异常  测试异常是否被捕获
      Mockito.when(userDao.save(name,phone)).thenThrow(new RuntimeException());
      try {
         registrationService.register(name,phone);
      }catch (Exception e){
        Assertions.assertTrue(e instanceof Exception);
      }
// ------------------  userDao.save   异常情况   测试   end  ------------------------
    }
}

As shown above: The first question mentioned above, how to inject member variables in the test class, can be done through @InjectMocks annotation.

The test coverage method is as follows:

9.png

The test results are as follows:

1. The right part will display the test coverage.

2. The green color of the real code represents the covered test, and the red represents the non-covered test.

11.png

If all test cases are 100% covered, the result is as follows:

12.png

In summary, the method of coverage testing is summarized as follows:

1. According to the business code, analyze all the situations that need to be tested.

2. Write specific test codes according to different test situations.

3. For each situation, you can write specific test codes, and then exhaust all the inventory situations by piling, assertion, etc.

question:

1. If the real code method level has throws Exception, then similarly, the test method must also throw exceptions at the method level, otherwise the test will report an error.

    @Test
    void register() throws Exception {

 2. The saved database is divided into normal and abnormal. Then test the normal branch first, and then test the abnormal branch. If the order is reversed, the test will throw an exception first, and the normal branch will not be executed, which will lead to incomplete test coverage.


The following is the supporting information. For friends who do [software testing], it should be the most comprehensive and complete preparation warehouse. This warehouse also accompanied me through the most difficult journey. I hope it can help you too!

Software testing interview applet

The software test question bank maxed out by millions of people! ! ! Who is who knows! ! ! The most comprehensive quiz mini program on the whole network, you can use your mobile phone to do the quizzes, on the subway or on the bus, roll it up!

The following interview question sections are covered:

1. Basic theory of software testing, 2. web, app, interface function testing, 3. network, 4. database, 5. linux

6. web, app, interface automation, 7. performance testing, 8. programming basics, 9. hr interview questions, 10. open test questions, 11. security testing, 12. computer basics

Information acquisition method:

Supongo que te gusta

Origin blog.csdn.net/myh919/article/details/131831333
Recomendado
Clasificación