使用 Mockit 做一些类似 JMockit 的 MockUp

Do with Mockit something like MockUp from JMockit

我想用 JMockit 做一些类似于 'MockUp' 的事情,但是使用 Mockito。

我想控制扩展 class 我想测试的 class 方法的行为。 但我有一个问题,那就是该方法是私有的,所以我认为我不能使用 Mockito,需要使用 PowerMock。

问题

Class A extends B{...}

Class B {
  private Header generateHeaderForServiceCall(c,d,f,g,h,j){...}
} 

在我的 Class ATest{ 在 @Before 中,我想模拟 generateHeaderForServiceCall(.....) 只为 return 创建一个默认的 Header对我来说。 }

因此,使用 JMockit 就像:

new MockUp<Controller>() {
  @Mock
  private Header generateHeaderForServiceCall(...) {
    return defaultHeader;
 }
};

我会更好地说明我的上下文:

public class B {
    private Header generateHeaderForServiceCall(Input A, Input B, Input c, Input D) throws ServiceException {
......
//do stuff
return header} 
}


public class A extends B {
    @Override
    public Response process(Request request) throws SOAException {
               //do stuff
        try {
            method_i_want_to_test(Input A, Input B);

            } catch (Exception t) {
                  throwCorrectException(t, logger);
     }
        return response;
 }

    protected Dossier method_i_want_to_test(Input A, Input B) throws 
       SOAException {
        ... //do stuff
        **Header** **header** = generateHeaderForServiceCall(Input A, Input 
             B,Input c, Input D);**

         // **doLogic** with header returned and return the result
    }
}

我想做什么:

private A aTest;
    @Before
    public void setUp() throws Exception {

        PowerMockito.mock(aTest);

 PowerMockito.doReturn(defaultHeader).when(aTest,"generateHeaderForServiceCall", params);
    }

因此,当我转到 method_i_want_to_test 并调用 generateHeaderForServiceCall 时,我只想获得默认值 header,而忽略方法的输入和逻辑。我想模拟这个方法,但是它 private/protected.

------------------------------------ 更新----------------------------

所以,我的 classA,我想测试的是:

    package mypackage;

    import package.ClassB;

    @Service
    public class ClassA extends ClassB implements Xinterface {


        @Inject
        public ClassA(InputA inputA,  InputB inputB,InputC inputC,  InputD inputD) {
            ...
        }

        @Override
        public ClassAResponse process(ClassARequest request) throws SOAException {
            ClassAResponse response = initResponse(inputA, request, new ClassAResponse());
            ClassAInput input = request.getInput();
            ClassAOutput output = new ClassAOutput();
            response.setOutput(output);

            try {

                /*  */
                method_i_want_to_test(request.getHeader(), numberInput);

            } catch (Exception t) {
                throwCorrectException(t, logger);
            }
            return response;
        }

        protected Dossier method_i_want_to_test(Header srcHeader, Long numberInput) throws SOAException {

            Header header = generateHeaderForServiceCall(inputA,srcHeader,inputF,inputJ,inputK);

            OtherServiceRequest request = new OtherServiceRequest();
            OtherServiceInput input = new OtherServiceInput();
            input.setNumber(numberInput);
            request.setInput(input);
            request.setHeader(header); // So, you can see the i need the result of generateHeaderForServiceCall method

            OtherServiceResponse response = OtherService.process(request);
            assertSucessfullResponse(response, "OtherService");

            return response;

        }

    }

我的 ClassB 包含私有和受保护的方法是:

    package otherPackage;
    ...

    public class ClassB {

        private Header generateHeaderForServiceCall(InputA inputA,Header srcHeader,InputF inputF,InputJ inputJ,InputK inputK) throws ServiceException {

            String[] nameInfo = QNameUtil.getServiceQNameInfo(inputA);

            String serviceVersion = auxMethod(inputJ, inputF);

            //... do more stuff

            return result;
        }
    }

还有我的 class 测试,我使用 PowerMock 测试私有方法,如果方法受保护则尝试使用 Mockito。之后我会解释当我 运行 两个测试时我得到了什么:

    package package;

    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.MockitoAnnotations;
    import org.powermock.api.mockito.PowerMockito;
    import org.powermock.core.classloader.annotations.PrepareForTest;
    import org.powermock.modules.junit4.PowerMockRunner;
    import static org.powermock.api.mockito.PowerMockito.doReturn;

    @RunWith(PowerMockRunner.class)
    @PrepareForTest(ClassA.class)
    public class MyTest {

        @InjectMocks
        private ClassA classA;
        @Mock
        private InputA inputA;
        @Mock
        private InputB inputB;
        @Mock
        private InputC inputC;
        @Mock
        private InputD inputD;

        @Before
        public void setUp() throws Exception {
            MockitoAnnotations.initMocks(this);
            classA = new classA( inputA,  inputB,inputC,  inputD);
        }

        @Test
        public void processPrivateMethod() throws Exception{
            defaultHeader = Aux.createDefaultHeader();

            //create the spy of my ClassA
            classA spy = PowerMockito.spy(classA);
            // Define what I want the method 'generateHeaderForServiceCall' returns when called
            doReturn(defaultHeader).when(spy, "generateHeaderForServiceCall", inputA,defaultHeader,inputF,inputJ,inputK);

            // I try to call the method 'method_i_want_to_test' with classA variable @Injected and with spy of ClassA
            //classA.method_i_want_to_test(defaultHeader,inputNumber);
            spy.method_i_want_to_test(defaultHeader,inputNumber);

        }
    }

1 - 当我 运行 这个 processPrivateMethod 在调试方法中测试时,当 generateHeaderForServiceCall 被调用时,它会尝试执行该方法的逻辑并失败,因为 header 是一个基本方法。但我尝试做的是模拟这个,只是 return 默认 Header 没有逻辑。

2- 如果我将 generateHeaderForServiceCall 更改为像 ClassB 的某些方法一样进行保护,并为此使用 mockito:

    @Test
        public void processProtectedMethod() throws Exception{
            defaultHeader = JUnitTestUtil.createDefaultHeader();
            when(classA.generateHeaderForServiceCall(inputA,defaultHeader,"ccccccc","dxdx",5464564)).thenReturn(defaultHeader);

            classA.method_i_want_to_test(defaultHeader,inputNumber);

        }

但它 return 一个错误,因为该方法是受保护的(如果它是私有的并且我使用 mockito,则同样的错误)。

Error: java: generateHeaderForServiceCall(....) has protected access in package

尝试次数:

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

        // Partial mock to mock methods in parent class
        child = new ClasseA(...){
            @Override
            protected Header generateHeaderForServiceCall(...) throws ServiceException {
                //mock logic here
                return aux.createDefaultHeader();;
            }
        };
    }

    @Test
    public void processPrivateMethod() throws Exception{
        defaultHeader = aux.createDefaultHeader();


        //when
        Dossier bdoo = child.method_i_want_to_test(...);

    } 

2-

@Test
    public void processPrivateMethod() throws Exception{
        defaultHeader = JUnitTestUtil.createDefaultHeader();

        child = PowerMockito.spy(new ClasseA(...));


       PowerMockito.doReturn(defaultHeader).when(child, "generateHeaderForServiceCall", context,defaultHeader,"ccccccc","dxdx",5464564);

        //when
        Dossier bdoo = child.method_i_want_to_test(...);
    }

3-

@Test
    public void processPrivateMethod() throws Exception{
        defaultHeader = JUnitTestUtil.createDefaultHeader();

        child = PowerMockito.spy(new ClassA(...));

        father = PowerMockito.spy(new ClasseB());

        PowerMockito.doReturn(defaultHeader).when(father, "generateHeaderForServiceCall", context,defaultHeader,"ccccccc","dxdx",5464564);

        //when
        Dossier bdoo = child.method_i_want_to_test(...);
    }

没有人做我想做的事。全部进入 classB 中的 generateHeaderForServiceCall 方法并尝试执行内部逻辑。 谢谢

听起来您正在寻找 spy


So, can i do with with Mockito? Do i need to use PowerMock?

如果它是私有的你需要使用 PowerMockito, 如果其受保护的 Mockito 可以处理它。

Can i use Mockito and PowerMockito together?

PowerMockito 建立在 Mockito 的基础上,所以是的。


请注意 spy 应谨慎使用,例如用于测试遗留代码。 通常的建议是重构您的代码。

@PrepareForTest Annotation 需要包含 class 字节码被修改的地方 Class A.


使用 PowerMockitoJUnit4 进行模拟的私有方法:

这里是一个使用 String return 而不是 Header 的简化示例:

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Spy;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(Test.A.class)
public class Test {

    static class A extends B {
        public String process() {
            return "y" + method_i_want_to_test();
        }
    }

    static class B {
        private String generateHeaderForServiceCall() {
            return "abc";
        }

        protected String method_i_want_to_test() {
            return "x" + generateHeaderForServiceCall();
        }
    }

    @Spy
    A classUnderTest = new A();

    @Test
    public void testCustomExceptionIsThrown() throws Exception {

        PowerMockito.doReturn("123").when(classUnderTest, "generateHeaderForServiceCall");
        Assert.assertEquals("yx123", classUnderTest.process());
    }
}

使用 MockitoJUnit5 进行模拟的受保护方法:

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class Test {

    static class A extends B {
        public String test() {
            return generateHeaderForServiceCall();
        }
    }

    static class B {
        protected String generateHeaderForServiceCall() {
            return "abc";
        }
    }

    @Spy
    A classUnderTest;

    @Test
    public void testCustomExceptionIsThrown() throws Exception {

        Mockito.when(classUnderTest.generateHeaderForServiceCall()).thenReturn("123");
        Assertions.assertEquals("123", classUnderTest.test());
    }
}