SoFunction
Updated on 2025-03-02

Java Springboot Backend uses the Mockito library to analyze unit test process

1 Why use mock for unit testing

If you use SpringbootTest for unit testing, then the spring service will be started, the container and bean will be generated and all external resources or dependencies will be loaded. It is time-consuming to start when the project is high. Obviously, there is some big trouble to start the entire project for a small unit test.

In addition, sometimes some external dependencies cannot be retrieved (such as database connection, cache, or interface acquisition problems), andUnit testing aims to test a specific method of a certain class. It tests the functional logic of the method and should not focus on the objects it depends on., in case the methods at each level are called too deep for each other during testing (the method called should be tested in its own unit test), it is equivalent to focusing only on the width of the test method and not on its depth.

Therefore, for external classes or methods used in the method to be tested, you can consider using mock objects to simulate their calls and customize the returned values.This allows unit tests to proceed normally without executing the real call method, this is the principle of mock. Using mock can well separate the method to be tested from its dependencies, reducing the coupling between modules, and is very suitable for scenarios where external resources are difficult to construct or method calls are too deep.

2 Notes on using mock

  • Spring is not started when unit testing through mock, then the container will not be loaded and beans will be generated, and the dependencies cannot be obtained through automatic injection. You need to use mock annotation
  • If the relevant functions of the test are related to spring (such as AOP), then SpringbootTest is still necessary
  • When mock is enabled, it can be regarded as starting a normal Java project. The code that can be run in a normal project can also be run in a mock.
  • During unit testing, not all external methods called or used external classes require mock. For example, entity classes can be directly new, and tool class methods can be run directly (tool classes that do not have external dependencies, such as tool classes used for data format processing, usually call their static methods). There is no need to use mock.

3 mock usage process

3.1 Pre-test configuration

Using mock first, you need to import the relevant dependencies in the pom file, and then you can use@MockOr the mock method of the Mockito library to generate mock objects if used@MockYou need to add the following annotations firstorCode:

  • Add before the test class@RunWith()or@ExtendWith()
  • Add the following method to the test class:
//Preparation before testing, you can perform some basic configuration here@BeforeEach
    void setUp(){
        (this); //Open mock and use it with @Mock}

3.2 Inject the class to be tested and simulate the variables used in it

If the test class isUserService, then in its test classUserServiceTestAnnotations are required@InjectMocksinjectionUserServiceThe function of this annotation is to automatically create and inject an instance of a class, and mark it as@MockThe dependency injects into this instance, note that@InjectMocksOnly class can be injected.

@InjectMocks
UserService userService;

3.2.1 Simulate member variables

Member variables of the class to be tested need to be used@MockAnnotation injection, cannot be passed()The method generates a mock object. The member mock object generated by the mock() method will report a null pointer error when executed, and the mock object is not really generated.
likeUserServiceThere are member variables inUserMapper, then use the following code injection:

@Mock
UserMapper

Simply put, it is toUserServiceAll member variables in it are copied directly toTestand then@AutowiredChange to@MockJust do it.

3.2.2 Simulating static objects

When a static method of a certain class needs to be called in the code, it is necessary to usemockStatic()The method generates a simulated static object, if the following code exists in the method to be tested:

("A_END"); //DictUtilThere are some external dependencies in the class,Therefore, simulation is needed

Then first, it needs to be generatedDictUtilSimulated static objects:

mockStatic();  // Both upper and lower writing methods are OKMockedStatic<DictUtil> dictUtilMockedStatic = mockStatic();

3.2.3 Simulate normal variables

For some intermediate variables generated in the code, and when you need to use these variables for method calls in the later stage, you need to use mock to simulate the intermediate variables first, such as if there is code in the method to be tested:

().getName("12345");

Then you need to simulate it firstUserCacheAn example ofWhen driving piles, you cannot pile them simultaneously for continuously called methods, and you need to create intermediate variables.), can be used at this time@MockOr mock method:

// 1. Inject as a test class member variable@Mock
UserCache userCache;
// 2. Directly simulate in the test methodUserCache userCache = ();

3.3 Piling simulation method call behavior

3.3.1 Non-static method pile driving

Piling means simulating the parameter transmission of the called method and setting the return value, the number of parameters and return values ​​need to correspond to the original method. After piling, it will not be called, and the execution results of the real method will be ignored. It is generally used().thenReturn()Perform pile driving.
If the code exists in the test class method:

User user = ("0000");

Then the pile driving in the test method can be:

//Entity class does not require mock, it can be generated directly, and then assign values ​​using the setter methodUser user = new User();
// 1. The pile driving parameter is fixed to "0000". The pile driving failure is when the parameter in the source code call is not "0000".(("0000")).thenReturn(user)
// 2. For the robustness of the code, the () method is usually used to receive parameters, and different types are specified according to different xxx. As long as any parameter of this type is a stake, you can drive((())).thenReturn(user)

3.3.2 Static method of pile driving

With original code().getName("12345");As an example, there are two ways to write static pile driving:
1) Ordinary writing

UserCache userCache = ();
mockStatic();  
(()).thenReturn(userCache);
((())).thenReturn("TestName");

2) Lambda writing method - static method call without parameters

UserCache userCache = ();
MockedStatic<UserCache> userCacheMockedStatic = mockStatic();
//1. When there is no parameter in the static method, you can use :: to make method calls(UserCache::getInstance).thenReturn(userCache);
//2. You can also use lambda writing method and use . to call it(() -> ()).thenReturn(userCache);
//3. The following writing method cannot be used://(()).thenReturn(userCache);
((())).thenReturn("TestName");

3) Lambda writing method - static method call with parameters
If the abovegetInstanceThe method has an int parameter, so you can use the normal writing method in 1) to perform pile driving, or use the method of 2)-2, and then use()Pass the parameters, but cannot use the writing method of 2)-1 or 2)-3.

3.3.3 Maven test static simulation error problem

There is no problem when running a test separately, but when executing the test collectively, the following error will appear:

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

This is because more than two test methods use the same static mock object, such as both tests haveMockedStatic<UserCache> userCacheMockedStatic = mockStatic(), while static mock objects are not allowed to be public, so each static mock object needs to be closed after it is executed, so the next method can continue to use it, so you need to write at the end of each Test:()
becauseMockedStatic<T>The parent interface inheritsAutoCloseableInterface, so you can use the try-resources statement to achieve automatic closing of resources:

try(
	// mockStatic(); // java8 does not support this writing method	MockedStatic&lt;UserCache&gt; userCacheMockedStatic = mockStatic();
	// ...Other static simulation object declaration){
    //Other test code}

You can also choose to use try-catch-finally to manually close it in the finally statement:

try(
	MockedStatic&lt;UserCache&gt; userCacheMockedStatic = mockStatic();
	// …Other test codes)finally{
    ();
}

3.4 Assertion and judgment test results

To detect the results of the test, an assertion is requiredAssertionsMake judgments and according to actual test requirements:

User user = ("123");
User expectUsr = new User("123");
(expectUsr ,user)://Judge whether the test result user is consistent with the expected object(user)://Judge test resultsuserIs it not empty

This is the article about the unit testing process of using the Mockito library in the Java Springboot backend. For more related Springboot Mockito unit testing content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!