19

I have a Java class named, MyClass, that I want to test with JUnit. The public method, methodA, that I want to test calls a private method, methodB, in the same class to determine which conditional path to follow. My goal is to write JUnit tests for the different paths in methodA. Also, methodB calls a service, so I do not want it to actually be executed when I run the JUnit tests.

What is the best way to mock methodB and control its return so that I can test different paths for 'methodA'?

I prefer to use JMockit when writing mocks, so I am specifically interested in any answer that applies to JMockit.

Here is my example class:

public class MyClass  {

    public String methodA(CustomObject object1, CustomObject object2)  {

        if(methodB(object1, object2))  {
            // Do something.
            return "Result";
        }

        // Do something different.
        return "Different Result";

    }

    private boolean methodB(CustomObject custObject1, CustomObject custObject2)  {

        /* For the sake of this example, assume the CustomObject.getSomething()
         * method makes a service call and therefore is placed in this separate
         * method so that later an integration test can be written.
         */
        Something thing1 = cobject1.getSomething();
        Something thing2 = cobject2.getSomething();

        if(thing1 == thing2)  {
            return true;
        }
        return false;
    }

}

This is what I have so far:

public class MyClassTest  {
    MyClass myClass = new MyClass();

    @Test
    public void test_MyClass_methodA_enters_if_condition()  {
        CustomObject object1 = new CustomObject("input1");
        CustomObject object2 = new CustomObject("input2");

        //  How do I mock out methodB here to return true?

        assertEquals(myClass.methodA(object1, object2), "Result");
    }

    @Test
    public void test_MyClass_methodA_skips_if_condition()  {
        CustomObject object1 = new CustomObject("input1");
        CustomObject object2 = new CustomObject("input2");

        //  How do I mock out methodB here to return false?

        assertEquals(myClass.methodA(object1, object2), "Different Result");
    }

}

Thanks!

Kingand
  • 627
  • 1
  • 5
  • 12
  • For this scenario, make that method package private i.e. 'default' access specifier. And I use EasyMock#createMockBuilder() to create partial mock so i can mock that private method. Don't know equivalent for your JMockit. – deepakraut Oct 31 '13 at 07:00
  • Are you interested in the Easymock - Powermock solution? Otherwise I want to write it out for you. – Tom Jonckheere Oct 31 '13 at 07:56
  • See also http://stackoverflow.com/questions/250692/how-do-you-unit-test-private-methods – Raedwald Oct 31 '13 at 08:19
  • See also http://stackoverflow.com/questions/34571/whats-the-proper-way-to-test-a-class-with-private-methods-using-junit – Raedwald Oct 31 '13 at 17:34

5 Answers5

2

Do not be tempted to mock private methods, even if you can engaging in trickery to do so using a mocking tool. Private members are implementation details, which you should be free to change. Instead use the non-private API to exercise the class. If this is troublesome, consider moving the troublesome code into a different class, if it is not there already, and use dependency injection to inject a mock implementation of the troublesome code.

Raedwald
  • 46,613
  • 43
  • 151
  • 237
2

To give the answer you asked for (using JMockit's partial mocking):

public class MyClassTest
{
    @Tested MyClass myClass;

    @Test
    public void test_MyClass_methodA_enters_if_condition() {
        final CustomObject object1 = new CustomObject("input1");
        final CustomObject object2 = new CustomObject("input2");

        new NonStrictExpectations(myClass) {{
            invoke(myClass, "methodB", object1, object2); result = true;
        }};

        assertEquals("Result", myClass.methodA(object1, object2));
    }

    @Test
    public void test_MyClass_methodA_skips_if_condition() {
        final CustomObject object1 = new CustomObject("input1");
        final CustomObject object2 = new CustomObject("input2");

        new NonStrictExpectations(myClass) {{
            invoke(myClass, "methodB", object1, object2); result = false;
        }};

        assertEquals("Different Result", myClass.methodA(object1, object2));
    }
}

However, I would not recommend doing it like that. In general, private methods should not be mocked. Instead, mock the actual external dependency of your unit under test (the CustomObject in this case):

public class MyTestClass
{
    @Tested MyClass myClass;
    @Mocked CustomObject object1;
    @Mocked CustomObject object2;

    @Test
    public void test_MyClass_methodA_enters_if_condition() {
        new NonStrictExpectations() {{
            Something thing = new Something();
            object1.getSomething(); result = thing;
            object2.getSomething(); result = thing;
        }};

        assertEquals("Result", myClass.methodA(object1, object2));
    }

    @Test
    public void test_MyClass_methodA_skips_if_condition() {
        new NonStrictExpectations() {{
            object1.getSomething(); result = new Something();
            object2.getSomething(); result = new Something();
        }};

        assertEquals("Different Result", myClass.methodA(object1, object2));
    }
}
Rogério
  • 16,171
  • 2
  • 50
  • 63
  • Thank you this answered my question! I used the first option here, although I completely agree that mocking private methods is not recommended. In my case, the `Something` object cannot be instantiated without following a chain of legacy code that calls several services. Since `methodB` checks the value of a specific field on `thing1` and `thing2`, it was just easier to mock out `methodB` and force a desired return than to try to mock `Something` or `CustomObject`. – Kingand Oct 31 '13 at 16:46
  • sad to say that but the Expectations support for mocking private methods is removed since JMockit 1.23, you have to use a MockUp now. – dag Sep 14 '16 at 11:25
0

Make methodB a member of a separate class, and have a private reference to that class within MyClass.

public class MyClass  {
    private MyOtherClass otherObject = new MyOtherClass();

    public String methodA(CustomObject object1, CustomObject object2)  {

        if(otherObject.methodB(object1, object2))  {
            // Do something.
            return "Result";
        }

        // Do something different.
        return "Different Result";

    }
}

class MyOtherClass {
    public boolean methodB(CustomObject custObject1, CustomObject custObject2)  {
        // Yada yada code
    }
}

Personally, I usually only test public methods and look at coverage reports to ensure that all paths have been visited in my private methods. If I really need to test a private method, that's a smell that requires a refactoring as I have above.

You could also use reflection, but I'd feel dirty doing that. If you REALLY want the solution to that let me know and I'll add it to this answer.

mikeslattery
  • 4,039
  • 1
  • 19
  • 14
  • My example code may have be misleading. My goal is to test the functionality in the `//Do Something` part of `methodA`. I am looking for a way to force `methodA` to enter the if statement so that I can test the code within it. I agree that if was trying to test `methodB`, then that would be an indication I may need to refactor. – Kingand Oct 31 '13 at 16:57
0
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest({ MyClass.class })
public class MyClassTest {

// Class Under Test
MyClass cut;

@Before
public void setUp() {

    // Create a new instance of the service under test (SUT).
    cut = new MyClass();

    // Common Setup
    // TODO
}

@Test
public void testMethodA() throws Exception {

    /* Initialization */
    CustomObject object2 = PowerMock.createNiceMock(CustomObject.class);
    CustomObject object1 = PowerMock.createNiceMock(CustomObject.class);

    MyClass partialMockCUT = PowerMock.createPartialMock(MyClass.class,
            "methodB");
    long response = 1;

    /* Mock Setup */
    PowerMock
            .expectPrivate(partialMockCUT, "methodB",
                    EasyMock.isA(CustomObject.class),
                    EasyMock.isA(CustomObject.class)).andReturn(true)
            .anyTimes();

    /* Mock Setup */

    /* Activate the Mocks */
    PowerMock.replayAll();

    /* Test Method */

    String result = partialMockCUT.methodA(object1, object2);

    /* Asserts */
    Assert.assertNotNull(result);
    PowerMock.verifyAll();

}

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

To mock the private method, you need powermock
The sample code will be like this, but I haven't run it.

    import org.mockito.Mockito;
    import org.powermock.api.mockito.PowerMockito;
    import org.powermock.modules.junit4.PowerMockRunner;

    @RunWith (PowerMockRunner.class)
    public class MyClassTest  {

        @Test
        public void test_MyClass_methodA_enters_if_condition()  {
            final MyClass myClass = Mockito.mock (MyClass.class);
            CustomObject object1 = new CustomObject("input1");
            CustomObject object2 = new CustomObject("input2");
            Mockito.when (myClass.methodB(object1, object2)).thenReturn (true);
            Mockito.when (myClass.methodA(object1, object2)).thenCallRealMethod ();

            assertEquals(myClass.methodA(object1, object2), "Result");
        }
    }
Lifecube
  • 1,198
  • 8
  • 11
  • 1
    I don't think this would compile. The compiler would complain about `methodB()` being private. There other means in PowerMock to call private methods that take a string argument for the method name. It gets kinda ugly, tho. – mikeslattery Oct 31 '13 at 06:30
  • You are right, I give the sample for not private methods. But this link could help. http://stackoverflow.com/questions/7803944/how-to-mock-private-method-for-testing-using-powermock – Lifecube Oct 31 '13 at 06:49