[英]Mock private method in the same class that is being tested
我有一个名为MyClass
的Java类,我想用JUnit进行测试。 公共方法methodA
,我想测试调用私有方法, methodB
,在同级别来确定要遵循的条件路径。 我的目标是为methodA
的不同路径编写JUnit测试。 此外, 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");
}
}
谢谢!
不要试图模仿私有方法,即使你可以使用模拟工具进行欺骗。 私有成员是实施细节,您可以自由更改。 而是使用非私有API来练习该类。 如果这很麻烦,可以考虑将麻烦的代码移到另一个类中,如果它已经不存在,并使用依赖注入来注入麻烦代码的模拟实现。
给出你要求的答案(使用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));
}
}
使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
}
}
就个人而言,我通常只测试公共方法并查看覆盖率报告,以确保在我的私有方法中访问过所有路径。 如果我真的需要测试一个私有方法,那就是需要重构的气味,就像我上面所说的那样。
你也可以使用反射,但我觉得这样做很脏。 如果您真的想要解决方案,请告诉我,我会将其添加到此答案中。
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();
}
}
要模拟私有方法,您需要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");
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.