How to mock private methods of current class

background

basic knowledge

Mockito unit testing: Its approach is to mock out all external dependencies of the current class to ensure that there are no problems with your own code. For example, if there is a problem with the database query statement, the unit test will not test it. Because it is directly mocked, it will not actually check the database. From this point of view, it seems a bit unreasonable. But, from a code perspective, there doesn't seem to be any problem. Because its goal is to ensure that its own code is normal, sql does not count. Besides unit testing, there are also test cases.
The process of writing unit tests: When writing unit tests, you need to analyze the code line by line. If it is an external dependency, then mock it and simulate a result yourself as a replacement. Otherwise continue analyzing the next line until the end of the code. Finally, verify whether the output results meet expectations under the specified input.

Why do we need to mock the private methods of the current class?

First, when we automatically generate unit test classes, it will only create test methods for public methods for us.
Insert image description here
When a public method of the class under test calls its private method, the unit test needs to analyze the mock line by line for the content in the private method. In particular, if another public method also calls this private method, that public method also needs to do the same thing. Obviously, from the perspective of test coverage, this private method has been covered, and there is no need to mock it line by line. At this time, it would be great if this private method could be mocked, right?

for example

Create the class under test

The private methods called are divided into two types: "with return" and "without return". We created pubFunction1 to call a private method with a return, and pubFunction2 to call a private method without a return.

package com.aliyu.service.demo.mock;

/**
 * 描述:单元测试如何mock当前类的其他私有方法调用
 * <p>作者: aliyu
 * <p>创建时间: 2023-01-10 10:57 下午
 */
public class MockitoDemo {
    
    

    /**
     * 模拟一个公共方法调用"不带返回"的私有方法
     */
    public void pubFunction1(){
    
    
        System.out.println("开始调用公共方法");
        withoutReturn();
        System.out.println("结束调用公共方法");
    }
    /**
     * 模拟一个公共方法调用"带返回"的私有方法
     */
    public void pubFunction2(){
    
    
        System.out.println("开始调用公共方法");
        withReturn();
        System.out.println("结束调用公共方法");
    }

    private void withoutReturn(){
    
    
        System.out.println("调用了'不带'返回的私有方法");
    }

    private String withReturn(){
    
    
        System.out.println("调用了'带'返回的私有方法");
        return "AAA";
    }
}

Create test class

Private method mock without return

Insert image description here
Note: It should also be noted that it must be junit4, otherwise an error will be reported. Will be mentioned later.
The complete code is as follows:

package com.aliyu.service.demo.mock;

import org.junit.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

/**
 * 描述:单元测试如何mock当前类的其他私有方法调用
 * <p>作者: aliyu
 * <p>创建时间: 2023-01-11 1:40 上午
 */
@RunWith(PowerMockRunner.class)
@PrepareForTest(MockitoDemo.class)
public class MockitoDemoTest {
    
    

    @InjectMocks
    private MockitoDemo mockitoDemo;

    @BeforeEach
    public void setUp() throws Exception {
    
    
        MockitoAnnotations.initMocks(this);
    }

    /**
     * 测试公共方法调用不带返回的私有方法
     * doNothing实现(暂不清楚和doAnswer的区别,倾向于使用doNothing)
     */
    @Test
    public void testPubFunction1() {
    
    
        MockitoDemo mockitoDemo1 = PowerMockito.spy(mockitoDemo);
        try {
    
    
            PowerMockito.doNothing().when(mockitoDemo1,"withoutReturn");
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        mockitoDemo1.pubFunction1();
    }

    /**
     * 测试公共方法调用不带返回的私有方法
     * doAnswer实现
     */
    @Test
    public void testPubFunction3() {
    
    
        MockitoDemo mockitoDemo1 = PowerMockito.spy(mockitoDemo);
        try{
    
    
            Answer answer=new Answer(){
    
    
                public Object answer(InvocationOnMock invocationOnMock) throws Throwable{
    
    
                    return null;
                }
            };
            PowerMockito.doAnswer(answer).when(mockitoDemo1,"withoutReturn");
        }catch(Exception e){
    
    
            e.printStackTrace();
        }
        mockitoDemo1.pubFunction1();
    }

}

Execute and view the results:
Insert image description here
Note: You can see that there is no output of "Private method called without 'return'", indicating that the mock has been successful.

Private method mock with return

The only difference from private methods without returns is the method used for mocking.
Insert image description here
Execute and view the results:
Insert image description here
Note: You can see that there is no output of "Private method with 'return' called", indicating that the mock has been successful.

other

If you use junit5 instead of junit4, an error will be reported.Insert image description here

The general meaning is that public methods have no return value, and you cannot create a pile with a return value. Obviously I am piling up private methods with returns.

When bringing a return value, doReturn must be in front, otherwise the mock will fail.

Insert image description here
Note: If doReturn fails to declare a return value, it will first execute the private method. .

Guess you like

Origin blog.csdn.net/mofsfely2/article/details/128644239