【问题标题】:Do with Mockit something like MockUp from JMockit使用 Mockit 做类似 JMockit 的 MockUp
【发布时间】:2020-02-21 23:54:37
【问题描述】:

我想做一些类似于 JMockit 中的“MockUp”但使用 Mockito 的东西。

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

问题

Class A extends B{...}

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

在我的 ATest 类中{ 在 @Before 我想模拟 generateHeaderForServiceCall(.....) 只返回为我创建的默认 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 时,我只想获得一个默认标头,而忽略方法的输入和逻辑。我想模拟这个方法,但它是私有的/受保护的。

  • 那么,我可以使用 Mockito 吗?

  • 我需要使用 PowerMock 吗?

  • 我可以同时使用 Mockito 和 PowerMockit 吗?

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

所以,我要测试的 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;
        }
    }

还有我的测试类,我在其中使用 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 没有逻辑。

2- 如果我像 ClassB 的某些方法一样将 generateHeaderForServiceCall 更改为 protected,并为此使用 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);

        }

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

错误:java: generateHeaderForServiceCall(....) 具有受保护的访问权限 包装内

尝试:

@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方法并尝试执行里面的逻辑。 谢谢

【问题讨论】:

  • 在标准的unit test 场景中,您不会真正改变non-public 方法的行为。虽然我在下面的回答从技术上解释了如何做到这一点,但您为什么觉得有必要这样做?
  • 嗨。我编辑了我的帖子,提供了一些信息以及为什么要模拟该方法。因为我想测试我的A类的一个方法,但是它需要B类的外部私有方法的结果,我需要模拟这个并控制他被调用时的返回。有意义吗?
  • 我不确定你想用external 表达什么,但在你之前的例子中,方法是private,在你最近的编辑中是protected。这会有所不同,因此请更加具体,并随时编辑或删除您问题的旧部分。
  • 是的,很抱歉。那么,单元测试就是关于我们的班级的,对吧?所有依赖项,我称之为“外部”,我们需要模拟它。在您的示例中,B 类是一个依赖项/外部项,您模拟它以返回“123”,对吗?但我的问题是 generateHeaderForServiceCall 不是公共方法,mockito 不能模拟私有方法。我将更改为 private 保护,我认为逻辑是相同的。实现起来更清楚?
  • 如果它的private 我假设method_i_want_to_testB 类的一部分(否则它不会编译)。我会更新我的答案。

标签: mocking mockito powermock mockups


【解决方案1】:

听起来您正在寻找spy


那么,我可以使用 Mockito 吗? 我需要使用 PowerMock 吗?

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

我可以同时使用 Mockito 和 PowerMockito 吗?

PowerMockito 是基于Mockito 构建的,所以是的。


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

@PrepareForTest 注解需要包含class,在这种情况下修改字节码Class A


使用PowerMockitoJUnit4 模拟私有方法:

这是一个使用String 返回而不是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());
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-14
    • 1970-01-01
    • 1970-01-01
    • 2015-12-24
    • 2016-07-11
    • 1970-01-01
    • 2015-02-27
    • 1970-01-01
    相关资源
    最近更新 更多