14

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

        }
    };
Varun
  • 209
  • 2
  • 5
  • 11
  • If you want to mock a private method, that sounds very much like you should move the functionality of that method to another class. – Icewind Jun 26 '14 at 10:11
  • This is private method of class under test. – Varun Jun 26 '14 at 10:14
  • And the private method should not be tested? – Icewind Jun 26 '14 at 10:15
  • I am testing a public method and want to mock the private method. – Varun Jun 26 '14 at 10:16
  • I do not believe this question should have been closed as the "duplicate" it references pertains to another issue - how to test a private method. This question clearly indicates the requirement to mock a private method, but the testing of a public method wherein the class under test is getting the private method mocked – Eric B. Nov 30 '18 at 16:38
  • 1
    Unfortunately since 1.46 there is no `invoke()` in `Deencapsulatio`n any more and in 1.47 the `Deencapsulation` class is removed. Unfortunately, `MockUp`s also cannot mock private methods. It's a pity. – kap Mar 25 '20 at 21:31

3 Answers3

15

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();
}
Thunderforge
  • 19,637
  • 18
  • 83
  • 130
Rogério
  • 16,171
  • 2
  • 50
  • 63
  • Thank you for support, with new Expectations(classToTestInstance) mocks the method two times after actual mehtod runs as you answered me in my another question, here I want to return "true" not the actual value. Here in the definition of methodToMock it is simply returns "true", I wanted to ask here if there is a logic in the definition of methodToMock after that the mehtodToMock returns some value and we want to return "true" from it. Then how should we mock? – Varun Jun 30 '14 at 07:38
  • Not sure if this is what you need, but you can always record a delegate object (`result = new Delegate() { ... };`) to return values based on some arbitrary logic. – Rogério Jun 30 '14 at 15:03
  • Thank you @Rogério. Please provide this comment as answer I will accept the answer. – Varun Jul 03 '14 at 12:26
  • The described way is not for partial, but for total mocking only. We can't use already existing class/instance methods and fields. Sometimes it can be useful, but very rarely. The answer of Kaushik is much more useful. – Gangnus Aug 11 '16 at 15:07
  • NonStrictExpectations are removed in JMockit 1.27 and the ability to mock private methods with Expectations was removed in JMockit 1.23. So You have to use MockUp s now see @Kaushik answer. – dag Sep 14 '16 at 11:35
13

This works for me:-

        new MockUp<ClassToTest>() {
            @Mock
            boolean methodToMock(int value) {
                return true;
            }
        };
Kaushik
  • 3,371
  • 3
  • 23
  • 32
  • 2
    @Gangnus Private method mocking (using @Mocked) has been discontinued since JMockit 1.23, and will be restricted when using `MockUp` in version 1.27. My answer is wrong because it still assumed that mocking a private method *could* be useful in certain cases; but it isn't. – Rogério Aug 11 '16 at 17:23
  • 2
    Many people wanted to find how to mock a private method. 12 of them even voted up some answers here. As for me, I searched for a solution, because I need it. And I found it. How can you say that nobody needs it? The problems of your answer are different - it is way too complex, when obviously people need something simple. And it is declaring partial mocking providing full mocking instead. I believe that you are terrific specialist in the theme. But for me your explanation is too complex. – Gangnus Aug 11 '16 at 19:10
  • I'm using jmockit 1.18, this works for me while @Rogério's answer doesn't – simomo Jan 19 '17 at 07:58
  • 2
    JMockit seems to have had a change of heart with respect to mocking private methods. Regardless of our individual opinions on that, perhaps that should have been done on a major release? On the topic of testing private methods, my opinion is that there is plenty of (legacy) code out there for which we need to write *characterization* tests effectively - mocking private methods being a very useful feature for that. I view JMockit as the tool of choice to deal with those situations - anything can be mocked, regardless of current notions of programming best practices. – beluchin Jan 19 '17 at 12:40
  • see the discussion on JMockit [issue 326](https://github.com/jmockit/jmockit1/issues/326) for more details on testing philosophy and the like hahaha – beluchin Jan 13 '18 at 14:38
  • @Rogério Can this concept/method still be used in 1.44+, or has it been eliminated? – Eric B. Nov 30 '18 at 16:48
  • 1
    @EricB. It's still there, and it's not going away. – Rogério Nov 30 '18 at 17:07
  • @EricB. On further thought, `MockUp` should no longer allow `@Mock`s for private methods/constructors. Every time I see this done, it's for a bad reason. Privates are mere implementation details, and tests shouldn't directly depend on them; a reasonable test can always mock/fake a dependency external to the class being tested, or fake a non-private method which calls the private one. – Rogério Dec 12 '18 at 15:37
2

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

        ....    

    }
}
Amit Kaneria
  • 5,466
  • 2
  • 35
  • 38