Mocking private method of class under test using JMockit

31,392

Solution 1

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

Solution 2

This works for me:-

        new MockUp<ClassToTest>() {
            @Mock
            boolean methodToMock(int value) {
                return true;
            }
        };

Solution 3

Here, you can over-ride a particular method of the testing class with mock behavior.

For the below code:

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 would be:

public class ClassToTestTest{

    @Test
    public void testMethodToTest(){

        new Mockup<ClassToTest>(){
            @Mock
            private boolean methodToMock(int value){
                return true;
            }
        };

        ....    

    }
}
Share:
31,392
Varun
Author by

Varun

Updated on December 02, 2020

Comments

  • Varun
    Varun over 3 years

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