Search code examples
javaunit-testingjunitmockingjmockit

Mocking private method of class under test using JMockit


I want to mock private method of a class under test but method return false first two times when the method is called after that it should return false. Here is the code what I tried. This is the class which is being tested

public class ClassToTest 
{
    public void methodToTest()
    {
        Integer integerInstance = new Integer(0);
        boolean returnValue= methodToMock(integerInstance);
        if(returnValue)
        {
            System.out.println("methodToMock returned true");
        }
        else
        {
            System.out.println("methodToMock returned true");
        }
        System.out.println();
    }
    private boolean methodToMock(int value)
    {
        return true;
    }
}

Test class

import org.junit.Test;
import static mockit.Deencapsulation.*;

import mockit.*;
public class TestAClass 
{
    @Tested ClassToTest classToTestInstance;
    @Test
    public void test1()
    {

        new NonStrictExpectations(classToTestInstance)
        {
            {
                invoke(classToTestInstance, "methodToMock", anyInt);
                returns(false);
                times = 2;

                invoke(classToTestInstance, "methodToMock", anyInt);
                returns(true);
                times = 1;

            }
        };

        classToTestInstance.methodToTest();
        classToTestInstance.methodToTest();
        classToTestInstance.methodToTest();

    }
}

I did this to get desired results

    final StringBuffer count = new StringBuffer("0");
    new NonStrictExpectations(classToTestInstance)
    {

        {
            invoke(classToTestInstance, "methodToMock", anyInt);
            result= new Delegate() 
            {
                boolean methodToMock(int value)
                {                   
                    count.replace(0, count.length(), Integer.valueOf(Integer.valueOf(count.toString())+1).toString());
                    if(Integer.valueOf(count.toString())==3)
                    {
                        return true;
                    }
                    return false;
                }
            };

        }
    };

Solution

  • Using Expectations (or StrictExpectations)

    Using a combination of Expectations and Deencapsulation.invoke(), you can partially mock the tested object:

    import org.junit.Test;
    import static mockit.Deencapsulation.*;
    import mockit.*;
    
    public class TestAClass
    {
        public static class ClassToTest 
        {
            public void methodToTest()
            {
                boolean returnValue = methodToMock(0);
                System.out.println("methodToMock returned " + returnValue);
            }
    
            private boolean methodToMock(int value) { return true; }
        }
    
        @Tested ClassToTest classToTestInstance;
    
        @Test
        public void partiallyMockTestedClass() {
            new Expectations(classToTestInstance) {{
                invoke(classToTestInstance, "methodToMock", anyInt);
                result = false;
                times = 2;
            }};
    
            classToTestInstance.methodToTest();
            classToTestInstance.methodToTest();
            classToTestInstance.methodToTest();
        }
    }
    

    The test above prints:

    methodToMock returned false
    methodToMock returned false
    methodToMock returned true
    

    In general, of course, we should avoid mocking private methods. That said, I have found in practice that it is sometimes useful to do so, typically when you have a private method which does something non-trivial and was already tested by another test; in such a case, mocking that private method in a second test (either for a different public method or a different path through the same public method) may be significantly easier than setting up necessary inputs/conditions.

    Using NonStrictExpectations (deprecated in JMockit 1.23)

    It's just as easy to write the test with a NonStrictExpectations (the original attempt by the OP didn't work only because the same non-strict expectation was recorded twice, with the second recording overriding the first):

    @Test
    public void partiallyMockTestedClass() {
        new NonStrictExpectations(classToTestInstance) {{
            invoke(classToTestInstance, "methodToMock", anyInt);
            returns(false, false, true);
        }};
    
        classToTestInstance.methodToTest();
        classToTestInstance.methodToTest();
        classToTestInstance.methodToTest();
    }
    

    Use a Delegate

    If more flexibility is needed, we can always record a Delegate-based result:

    @Test
    public void partiallyMockTestedClass() {
        new NonStrictExpectations(classToTestInstance) {{
            invoke(classToTestInstance, "methodToMock", anyInt);
    
            result = new Delegate() {
               boolean delegate() {
                   boolean nextValue = ...choose next return value somehow...
                   return nextValue;
               }
            }
        }};
    
        classToTestInstance.methodToTest();
        classToTestInstance.methodToTest();
        classToTestInstance.methodToTest();
    }