3

I need to mock a protected method in the parent class of my class under test but the parent class is in a different package so my test class cannot access that method so I cannot mock it. There's gotta be a solution for this issue without refactoring

I need to use Powermock and Mockito. Here's the JARs

  • mockito-all 1.10.8
  • powermock-core 1.6.1
  • powermock-module-junit4 1.6.1
  • powermock-api-mockito 1.6.1
  • junit 4.12

This is legacy code so I cannot refactor, but here's the simplified code.

Parent.java

package parent;

public class Parent {

    // Want to mock this protected parent method from different package
    protected String foo() {

        String someValue = null;

        // Logic setting someValue

        return someValue;
    }
}

Child.java

package child;

import parent.Parent;

public class Child extends Parent {

    String fooString = null;

    public String boo() {

        this.fooString = this.foo();

        String booString = null;

        // Logic setting booString

        return booString;
    }
}

ChildTest.java

package child;

import static org.mockito.Mockito.spy;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import parent.Parent;

@RunWith(PowerMockRunner.class)
@PrepareForTest({ Parent.class, Child.class })
public class ChildTest {

    // Class Under Test
    Child cut;

    @Before
    public void setUp() throws Exception {

        // Partial mock to mock methods in parent class
        cut = spy(new Child());

    }

    @Test
    public void testBoo() {

        // TODO: Need to mock cut.foo() but can't figure out how.

        // Following gives me this error: The method foo() from the type Parent is not visible
       Mockito.when(((Parent)cut).foo()).thenReturn("mockValue");

        // Test
        cut.boo();

        // Validations
        Assert.assertEquals(cut.fooString, "mockValue");

    }
}
javaPlease42
  • 4,699
  • 7
  • 36
  • 65

2 Answers2

6

Just create a new class for testing extending the class you want to test, and override your method there.

That would look something like that:

public class ChildForTest extends Child{
     @Override
     protected String foo() {
         //mock logic here
    }
}

Edit: If you want to avoid new class definition you can use anonymous class

@Before
public void setUp() throws Exception {

    // Partial mock to mock methods in parent class
    cut = new Child(){
        @Override
        protected String foo(){
            //mock logic here
            return "";
        }
    };
}
Mateusz Dryzek
  • 651
  • 4
  • 18
  • I do not want to refactor any Java. I only want to write a new Unit Test Class and leverage PowerMock and Mockito. – javaPlease42 Jan 08 '16 at 19:21
  • 1
    You don't have to refactor anything, you just create a new Class that will have this foo method mocked, and instead of using Child object in test you can use ChildForTest object, since the only difference will be the way the method foo() works – Mateusz Dryzek Jan 08 '16 at 19:23
  • But this is introducing a new class. All I am trying to do is cover all of Child.java without going into Parent.java. So I want to cover all of Child.java by mocking everything in Parent.java. – javaPlease42 Jan 08 '16 at 19:28
  • 1
    And you can do that using the solution I've sugested above. – Mateusz Dryzek Jan 08 '16 at 19:38
  • I don't want to Override the foo method, or fiddle with the Child instantiation. There should be a pure Mocking Framework solution for this issue. – javaPlease42 Jan 08 '16 at 19:48
  • 2
    Mocking framework are overrated and overused IMO. You could read about that in Uncle Bob's article https://blog.8thlight.com/uncle-bob/2014/05/10/WhenToMock.html (Write your own mocks. section) – Mateusz Dryzek Jan 08 '16 at 19:53
  • See http://stackoverflow.com/questions/8312212/mocking-protected-method#8312536 for the answer you want, but Mateusz Dryzek is right. It makes more sense to just subclass and override explicitly (which is what PowerMockito will do behind the scenes via some bytecode and/or proxy magic). His solution does not modify any production code, legacy or not. – aro_tech Jan 09 '16 at 07:30
3

You can use PowerMock to mocking no public method.

@RunWith(PowerMockRunner.class)
public class ChildTest {

    @Test
    public void testBoo() throws Exception {
        //given
        Child child = PowerMockito.spy(new Child());
        PowerMockito.when(child, "foo").thenReturn("mockValue");

        //when
        String boo = child.boo();

        //then
        Assert.assertEquals("boo+mockValue", boo);
    }
}
Mateusz Korwel
  • 1,118
  • 1
  • 8
  • 14