Dave
Dave

Reputation: 19260

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.

Upvotes: 61

Views: 131455

Answers (9)

Deepak
Deepak

Reputation: 1716

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

Upvotes: 0

Ahmet
Ahmet

Reputation: 968

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

Upvotes: 0

Debanshu Kundu
Debanshu Kundu

Reputation: 805

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

Upvotes: 5

Jess
Jess

Reputation: 3725

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.

Upvotes: -1

Nyefan
Nyefan

Reputation: 301

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!");
    }
}

Upvotes: 28

hoomb
hoomb

Reputation: 686

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

Upvotes: 15

Smart Coder
Smart Coder

Reputation: 1748

WhiteBox.invokeMethod() can be handy.

Upvotes: -1

Seb
Seb

Reputation: 977

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:

Upvotes: 0

John B
John B

Reputation: 32969

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.

Upvotes: 62

Related Questions