Search code examples
javaunit-testingjunitmockingjmockit

Mock private method in the same class that is being tested


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!


Solution

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