How do I use Mockito to mock a protected method?
Asked Answered
W

9

61

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.

Widmer answered 4/12, 2015 at 16:16 Comment(0)
F
63

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.
Finnie answered 4/12, 2015 at 16:52 Comment(5)
I went with option #3 (creating a class that has a public method that overrides the protected method, whcih just calls super.method())Widmer
method 1 does not seems to work when having same package name but source is in main folder and test classes are in test folderSearching
I was able to mock protected method only using Mockito and Java Reflection. Added answer here: https://mcmap.net/q/322581/-how-do-i-use-mockito-to-mock-a-protected-methodFefeal
For #3 what if the the mocked class calls another super class in its constructor?Southbound
@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?Finnie
A
28

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!");
    }
}
Athenaathenaeum answered 13/9, 2017 at 17:37 Comment(3)
The @Override doesn’t seem mandatory ? Also, wouldn’t a spy be more appropriate than a mock ?Imine
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://mcmap.net/q/322581/-how-do-i-use-mockito-to-mock-a-protected-methodFefeal
spy worked for me without override but test class need to be in the same package structure, so that protected method should be visible @SkippyleGrandGourouHalverson
M
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));
    }
}
Myrtlemyrvyn answered 6/12, 2018 at 8:50 Comment(3)
are you sure this works ? i got the same errorForint
@Forint would this help? github.com/hoomb/testdemoMyrtlemyrvyn
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://mcmap.net/q/322581/-how-do-i-use-mockito-to-mock-a-protected-methodFefeal
F
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);
Fefeal answered 16/9, 2021 at 19:25 Comment(5)
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.Finnie
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.Finnie
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://mcmap.net/q/324224/-mock-inherited-protected-method/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.Fefeal
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.Fefeal
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.Finnie
A
0

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:

Antipole answered 4/12, 2015 at 17:2 Comment(2)
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?Widmer
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).Finnie
R
0
MyService myService = new MyService() {
    MyService callProtectedMethod(String param1, String param2) {
        myMethod(param1, param2);
        return this;
    }
}.callProtectedMethod(param1, param2);

// your assertion here: trivial e.g. assertNotNull(myService);
Raynor answered 21/12, 2023 at 18:56 Comment(0)
H
0
package com.mypackage;
public class MyClass {
    protected String protectedMethod() {
        return "Can't touch this";
    }
    public String publicMethod() {
        return protectedMethod();
    }
}


package com.mypackage;

import org.junit.Before;
import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;


@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    private MyClass myClass;

    private MyClass spyMyClass;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        myClass = new MyClass();
        spyMyClass = spy(myClass); 
        doReturn("Something").when(spyMyClass)
                        .protectedMethod();
    }
    
    @Test
    testPublicMethod(){
        AssertThat(spyMyClass.publicMethod()).isEqualTo("Something"); 
    } 

}

doReturn and spy are important here

Halverson answered 18/7 at 5:29 Comment(0)
L
-1

WhiteBox.invokeMethod() can be handy.

Looper answered 27/7, 2018 at 13:51 Comment(0)
C
-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.

Cider answered 27/11, 2020 at 8:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.