0

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.

  • So, can i do with with Mockito?

  • Do i need to use PowerMock?

  • Can i use Mockito and PowerMockit together?

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

Lnoob
  • 97
  • 2
  • 2
  • 10
  • In a standard `unit test` scenario you would not really change the behaviour of `non-public` methods. While my answer below explains how to do that technically, why do you feel the need to do that? – second Oct 25 '19 at 21:11
  • Hi. I edit my post, with some information and why I want to mock that method. Because I want to test one method of my class A, but it need the result of an external private method from class B, and I need mock this and control the return when he is called. Make sense? – Lnoob Oct 26 '19 at 07:22
  • I am not sure what you want to express with `external`, but in your previous example the method is `private` and in your more recent edit its `protected`. That makes a difference, so please be more specific and feel free to edit or remove the old part of your question. – second Oct 26 '19 at 08:00
  • Yeah, sorrry for that. So, unit testing is all about our class right? All dependencies, i call as "external" and we need to mock that. In your example, class B is one dependency/external and you mock it for return "123" right? But my problem is that generateHeaderForServiceCall is not a public method and mockito cant mock private methods. I will change protected for private, i thought the logic is the same. Its more clear to realize? – Lnoob Oct 26 '19 at 09:13
  • If its `private` I assume that the `method_i_want_to_test` is part of class `B` (as it won't compile otherwise). I'll update my answer. – second Oct 26 '19 at 09:35
  • No, **method_i_want_to_test** is parte of class A but in is logic it call the method **generateHeaderForServiceCall** that is part of Class B, private and i need the result of it for test the first method. – Lnoob Oct 26 '19 at 09:50
  • I've update my answer. I guess the principle is the same. If that still does not match your case provide a `compilable` example of the involved classes. – second Oct 26 '19 at 09:52
  • If `generateHeaderForServiceCall` is private and part of class `B` class `A` won't be able to see it, hence you can not use it in the `method_i_want_to_test` that is part of class `A`. – second Oct 26 '19 at 10:03
  • Thanks for the answers, but i cant mock the service. I created a file with my real code and i will update my post with it (no real names) for you see and explain the errors. – Lnoob Oct 26 '19 at 11:12
  • Sorry, but I still don't see how your `ClassA` could possibly compile. Java should be unable to resolve `generateHeaderForServiceCall` as long as the method is `private`. – second Oct 26 '19 at 12:27
  • It compile. In my classB i have private and protected methods. This is a big project, and i was allocated to do unit tests to this. If you see the new MockUp() example with JMockit, that fix the problem to protected methods. But i need to use Mockito/PowerMock to that. Can you give me a example for a ClassB protected method with Mockito please? – Lnoob Oct 26 '19 at 12:41
  • Like if **generateHeaderForServiceCall** is a protected method. – Lnoob Oct 26 '19 at 12:44
  • If it compiles than your actual code must be different from the example you have added (thats just basic java visibility stuff). Anyway I restored the previous example with mockito from the edit history. – second Oct 26 '19 at 13:10
  • Thank you, i will try with protected. Just a question, why (@ExtendWith(MockitoExtension.class)) ? – Lnoob Oct 26 '19 at 13:13
  • And, can I create an @InjectMocks of my classA and in the code create a spy of it? Or I need to create with @Spy? – Lnoob Oct 26 '19 at 13:19
  • Yes you can. The `Annotations` are matching to the normal methods. However I read somewhere that its not recommeneded to use both annotations (`@InjectMocks` and `@Spy`) on the same object. If you do it manually it should be fine. – second Oct 26 '19 at 13:31
  • Ty. For protected methods its necessary use @ExtendWith(MockitoExtension.class? I have the Junit 4 :/ – Lnoob Oct 26 '19 at 13:33
  • I tried this [link](https://stackoverflow.com/questions/34684328/mock-protected-parent-method-when-the-parent-is-in-a-different-package) but I dont had sucess. It is exactly my problem with protected method in another package. I tried de override too, but noting... – Lnoob Oct 26 '19 at 15:18
  • If you look to the solution that i initially post `new MockUp() { @Mock private Header generateHeaderForServiceCall(...) { return defaultHeader; } };`, that is like a override of that method, but JMockit permit that with new MockUP. Can i do that with Mockito? – Lnoob Oct 26 '19 at 15:49
  • I update the code with 3 attempts without success – Lnoob Oct 26 '19 at 16:45
  • `@ExtendWith(MockitoExtension.class)` is the junit5 equivalent to `@RunWith(MockitoJUnitRunner.class)`. – second Oct 27 '19 at 13:55
  • First time you mention that your parent class is in a different package. I still think the key behind solving your issue is understanding how your real code for `ClassA` looks like. As long as you can not provide this information this discussion while go no where. (The linked `answer` should all work as well). – second Oct 27 '19 at 13:59
  • Thank you. I fix using the powermock @Spy and @PrepareForTest(value = {}) all class i need. For the protected one. – Lnoob Oct 27 '19 at 19:54
  • Now i find another step that no make sense for me in ClassA, he is calling an inteface `interfaceVariable.process(X)` but i cant mock that one. Firstly i override the method, but it still return NullpointerEx. – Lnoob Oct 27 '19 at 19:57
  • Anyway, thank you a lot. – Lnoob Oct 27 '19 at 19:57

1 Answers1

0

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());
    }
}
second
  • 4,069
  • 2
  • 9
  • 24