17

我有一个名为 的 Java 类MyClass,我想用 JUnit 对其进行测试。methodA我要测试的公共方法调用methodB同一类中的私有方法 ,以确定要遵循的条件路径。我的目标是为methodA. 另外,methodB调用服务,所以我不希望在运行 JUnit 测试时实际执行它。

模拟和控制其返回的最佳方法是什么,methodB以便我可以测试“methodA”的不同路径?

我更喜欢在编写模拟时使用 JMockit,所以我对任何适用于 JMockit 的答案都特别感兴趣。

这是我的示例类:

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

}

这是我到目前为止所拥有的:

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

}

谢谢!

4

5 回答 5

4

不要试图模拟私有方法,即使您可以使用模拟工具进行欺骗。私有成员是实现细节,您应该可以自由更改。而是使用非私有 API 来练习课程。如果这很麻烦,考虑将麻烦的代码移到不同的类中,如果它已经不存在,并使用依赖注入来注入麻烦代码的模拟实现。

于 2013-10-31T08:24:48.920 回答
2

要给出您要求的答案(使用 JMockit 的部分模拟):

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

但是,我建议这样做。通常,private不应嘲笑方法。相反,模拟被测单元的实际外部依赖项(CustomObject在这种情况下):

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));
    }
}
于 2013-10-31T10:46:39.633 回答
0

使 methodB 成为单独类的成员,并在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
    }
}

就个人而言,我通常只测试公共方法并查看覆盖率报告,以确保在我的私有方法中访问了所有路径。如果我真的需要测试一个私有方法,那就像我上面所说的那样需要重构。

你也可以使用反射,但我会觉得这样做很脏。如果您真的想要解决方案,请告诉我,我会将其添加到此答案中。

于 2013-10-31T06:09:56.610 回答
0
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();

}

}
于 2013-11-08T15:05:06.303 回答
-1

要模拟私有方法,你需要powermock
示例代码会是这样的,但我没有运行它。

    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");
        }
    }
于 2013-10-31T06:16:46.240 回答