55

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.

Dave
  • 15,639
  • 133
  • 442
  • 830

7 Answers7

58

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.
John B
  • 32,493
  • 6
  • 77
  • 98
  • 6
    I went with option #3 (creating a class that has a public method that overrides the protected method, whcih just calls super.method()) – Dave Dec 04 '15 at 19:56
  • 4
    method 1 does not seems to work when having same package name but source is in main folder and test classes are in test folder – poyger Aug 23 '18 at 10:49
  • I was able to mock protected method only using Mockito and Java Reflection. Added answer here: https://stackoverflow.com/a/69214093/2367394 – Debanshu Kundu Sep 16 '21 at 19:27
  • For #3 what if the the mocked class calls another super class in its constructor? – Saurav Shrivastav Feb 17 '22 at 07:12
  • @SauravShrivastav I would think it would still work because it is able to create the instance. Have you tried it? Are you getting an error? – John B Feb 17 '22 at 12:34
23

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!");
    }
}
Skippy le Grand Gourou
  • 6,976
  • 4
  • 60
  • 76
Nyefan
  • 251
  • 3
  • 10
  • 1
    The `@Override` doesn’t seem mandatory ? Also, wouldn’t a `spy` be more appropriate than a `mock` ? – Skippy le Grand Gourou Oct 05 '20 at 07:34
  • I was able to mock protected method only using Mockito and Java Reflection, and without need to override any class/method. Added answer here: https://stackoverflow.com/a/69214093/2367394 – Debanshu Kundu Sep 16 '21 at 19:38
15

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));
    }
}
Nyefan
  • 251
  • 3
  • 10
hoomb
  • 649
  • 2
  • 12
  • 24
  • are you sure this works ? i got the same error – tk_ Aug 05 '21 at 15:42
  • 1
    @tk_ would this help? https://github.com/hoomb/testdemo – hoomb Aug 13 '21 at 15:04
  • 1
    Above answer did not work for me. But I was able to mock protected method only using Mockito's doReturn() method and Java Reflection. Added answer here: https://stackoverflow.com/a/69214093/2367394 – Debanshu Kundu Sep 16 '21 at 19:36
4

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);
Debanshu Kundu
  • 785
  • 7
  • 18
  • If I am reading this correctly, this is invoking the method on the mock. What you have to remember is that the mock is generally not the class under test but a dependency in the class under test. So GENERALLY when dealing with a mock, you don't want to invoke the method but instead cause the desired behavior when the mock is invoked by the method under test. That said, the question is odd in that way because if the above is true, why would you mock a protected method instead of an invokable public method. – John B Sep 17 '21 at 20:40
  • Unless the protected method is in a super class of the class under test, in which case you might consider mocking the class under test. But again, you wouldn't be invoking the protected method in the test class, but in the method under test. Please let me know if I am not understanding more your solution works however. – John B Sep 17 '21 at 20:42
  • 1
    In my case (where I am using this technique), I have superclass with a protected method, I am testing the child class, and I want to mock the call to superclass's method. After looking through many SO questions and other blogs, I deduced this technique. With this my answer is more relevant to the question https://stackoverflow.com/q/34226209/2367394 (I have posted the answer there too). Although the same technique can be used for a class user test, even if the protected/private method is defined in the same class, by spying on the class. Hence I posted the answer here too. – Debanshu Kundu Sep 18 '21 at 14:55
  • Above being said, I understand that for protected methods defined in class under test, they should be directly mockable, given we typically define the test class in the same package where the class under test is defined. – Debanshu Kundu Sep 18 '21 at 14:56
  • Interesting and I appreciate being pointed to ReflectionSupport. That said, still feels like "test smell" to mock the class under test IMHO. I also feel that this code is so obscure that another developer looking at this in 2 years would have no idea what is being done. I feel the other solutions suggested are more readable / maintainable: extending the class and overriding the protected method. – John B Sep 19 '21 at 15:41
1

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:

Community
  • 1
  • 1
Seb
  • 959
  • 16
  • 29
  • With regards to the links you posted, I'm not making the link between how reflection would be tied to Mockito. Can you post some example code to show how a protected method can be mocked with Mockito? – Dave Dec 04 '15 at 17:44
  • He is not calling the protected method (for which reflection would be helpful) but trying to mock the protected method (for which it is not). – John B Dec 04 '15 at 20:38
0

WhiteBox.invokeMethod() can be handy.

Smart Coder
  • 1,435
  • 19
  • 19
-1
public class Test extend TargetClass{
    @Override
    protected Object method(...) {
        return [ValueYouWant];
    }  
}

In Spring, you can set it high high-priority like this:

@TestConfiguration
public class Config {

    @Profile({"..."})
    @Bean("...")
    @Primary  // <------ high-priority
    public TargetClass TargetClass(){
        return new TargetClass() {
            @Override
            protected WPayResponse validate(...)  {
                return null;
            }
        };
    }
}

It is the same to override the origin bean.

Jess Chen
  • 3,136
  • 1
  • 26
  • 35