Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use Mockito to mock a protected method?

I’m using Mockito 1.9.5. How do I mock what is coming back from a protected method? I have this protected method …

protected JSONObject myMethod(final String param1, final String param2)
{
…
}

However, when I attempt to do this in JUnit:

    final MyService mymock = Mockito.mock(MyService.class, Mockito.CALLS_REAL_METHODS);        
    final String pararm1 = “param1”;
    Mockito.doReturn(myData).when(mymock).myMethod(param1, param2);

On the last line, I get a compilation error “The method ‘myMethod’ is not visible.” How do I use Mockito to mock protected methods? I’m open to upgrading my version if that’s the answer.

like image 423
Dave Avatar asked Dec 04 '15 16:12

Dave


People also ask

How do you test protected methods in junit?

To test a protected method using junit and mockito, in the test class (the class used to test the method), create a “child class” that extends the protagonist class and merely overrides the protagonist method to make it public so as to give access to the method to the test class, and then write tests against this child ...

Can Mockito mock private methods?

For Mockito, there is no direct support to mock private and static methods. In order to test private methods, you will need to refactor the code to change the access to protected (or package) and you will have to avoid static/final methods.


5 Answers

This is not an issue with Mockito, but with plain old java. From where you are calling the method, you don't have visibility. That is why it is a compile-time issue instead of a run-time issue.

A couple options:

  • declare your test in the same package as the mocked class
  • change the visibilty of the method if you can
  • create a local (inner) class that extends the mocked class, then mock this local class. Since the class would be local, you would have visibility to the method.
like image 141
John B Avatar answered Oct 09 '22 09:10

John B


Responding to the request for a code sample of option 3 from John B's answer:


public class MyClass {
    protected String protectedMethod() {
        return "Can't touch this";
    }
    public String publicMethod() {
        return protectedMethod();
    }
}

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    class MyClassMock extends MyClass {
        @Override
        public String protectedMethod() {
            return "You can see me now!";
        }
    }

    @Mock
    MyClassMock myClass = mock(MyClassMock.class);

    @Test
    public void myClassPublicMethodTest() {
        when(myClass.publicMethod()).thenCallRealMethod();
        when(myClass.protectedMethod()).thenReturn("jk!");
    }
}
like image 25
Nyefan Avatar answered Oct 09 '22 10:10

Nyefan


You can use Spring's ReflectionTestUtils to use your class as it is and without needing of change it just for tests or wrap it in another class.

public class MyService {
    protected JSONObject myProtectedMethod(final String param1, final String param2) {
        return new JSONObject();
    }

    public JSONObject myPublicMethod(final String param1) {
        return new JSONObject();
    }
}

And then in Test

@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
    @Mock
    private MyService myService;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        when(myService.myPublicMethod(anyString())).thenReturn(mock(JSONObject.class));
        when(ReflectionTestUtils.invokeMethod(myService, "myProtectedMethod", anyString(), anyString())).thenReturn(mock(JSONObject.class));
    }
}
like image 25
hoomb Avatar answered Oct 09 '22 08:10

hoomb


John B is right, this is because the method you're trying to test is protected, it's not a problem with Mockito.

Another option on top of the ones he has listed would be to use reflection to gain access to the method. This will allow you to avoid changing the method you are testing, and avoid changing the pattern you use to write tests, and where you store these tests. I've had to do this myself for some tests where I was not allowed to change the existing code base which included a large number of private methods that needed to be unit tested.

These links explain Reflection and how to use it very well, so I will link to them rather than copy:

  • What is reflection and whit is it useful
  • How to test a class that has private methods, fields, or inner classes
like image 20
Seb Avatar answered Oct 09 '22 10:10

Seb


Something like following worked for me, using doReturn() and Junit5's ReflectionSupport.

[Note: I tested on Mockito 3.12.4]

ReflectionSupport.invokeMethod(
        mymock.getClass()
//              .getSuperclass()     // Uncomment this, if the protected method defined in the parent class.
                .getDeclaredMethod("myMethod", String.class, String.class),
        doReturn(myData).when(mymock),
        param1,
        param2);
like image 1
Debanshu Kundu Avatar answered Oct 09 '22 08:10

Debanshu Kundu