mockingmockitopowermockmockups

Do with Mockit something like MockUp from JMockit


I want to do something similar to 'MockUp' from JMockit but with Mockito.

I want to control the comportment of a method of a class that extends the class i want to test. But i have one problem that is the method is private, so i think i cant go with Mockito, and need use PowerMock.

The Problem

Class A extends B{...}

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

In my Class ATest{ In @Before i want to mock generateHeaderForServiceCall(.....) for just return a default Header created for me. }

So, using JMockit is like:

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

I will specify better my context:

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
    }
}

What im trying to do:

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

        PowerMockito.mock(aTest);

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

So, when i go to method_i_want_to_test and call the generateHeaderForServiceCall i just want to get a default header, and ignore the inputs and the logic of the method. I want to mock this method, but its private/protected.

-------------------------------------- UPDATE ------------------------------

So, my classA, that i want to test is that :

    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;

        }

    }

My ClassB that contains private and protected methods is that:

    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;
        }
    }

And my class of test, where i do test for private methods with PowerMock and try with Mockito if the method is protected. After that i will explain what i got when i run both tests:

    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 - when I Run this the processPrivateMethod test in debug method, when the generateHeaderForServiceCall is called, it tries execute the logic of the method and fails because the header is a basic one. But what i try to do is mock this and just return the default Header without logic.

2- If i change the generateHeaderForServiceCall for protected like some methods of ClassB, and use mockito for that:

    @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);

        }

But it return an error because the method is protected ( same error if it is private and i use mockito).

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

Attempts:

@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(...);
    }

Noone do what i want. All go into generateHeaderForServiceCall method in classB and trying do the logic inside. Thanks


Solution

  • Sounds like you are looking for a spy.


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

    If its private you need to use PowerMockito, if its protected Mockito alone could handle it.

    Can i use Mockito and PowerMockito together?

    PowerMockito is build upon Mockito, so yes.


    Note that spy should be used sparingly, for example for testing legacy code. The usual recommondation would be to refactor your code.

    The @PrepareForTest Annotation needs to include the class where the bytecode is modified in this case Class A.


    Private method mocking with PowerMockito and JUnit4:

    Here a simplified example using a String return instead of a 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());
        }
    }
    

    Protected method mocking with Mockito and JUnit5:

    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());
        }
    }