Mock private method in the same class that is being tested

后端 未结 5 1513
余生分开走
余生分开走 2021-02-02 14:49

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, meth

相关标签:
5条回答
  • 2021-02-02 15:01

    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));
        }
    }
    
    0 讨论(0)
  • 2021-02-02 15:02

    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.

    0 讨论(0)
  • 2021-02-02 15:06

    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");
            }
        }
    
    0 讨论(0)
  • 2021-02-02 15:13

    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.

    0 讨论(0)
  • 2021-02-02 15:26
    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();
    
    }
    
    }
    
    0 讨论(0)
提交回复
热议问题