如何使用JUnit测试具有内部私有方法、字段或嵌套类的类?
仅仅为了能够运行测试而更改方法的访问修饰符似乎是不好的。
如何使用JUnit测试具有内部私有方法、字段或嵌套类的类?
仅仅为了能够运行测试而更改方法的访问修饰符似乎是不好的。
当前回答
JML具有spec_public注释注释语法,允许您在测试期间将方法指定为public:
private /*@ spec_public @*/ int methodName(){
...
}
2.4隐私修改器和可见性中讨论了此语法。还有一个将JML规范转换为JUnit测试的程序。我不确定它的工作情况如何,或者它的功能是什么,但似乎没有必要,因为JML本身就是一个可行的测试框架。
其他回答
首先,我要抛出一个问题:为什么你的私人成员需要隔离测试?它们是否如此复杂,提供了如此复杂的行为,以至于需要在公共表面之外进行测试?这是单元测试,而不是“代码行”测试。别为小事操心。
如果它们是那么大,足够大,以至于这些私有成员都是一个复杂度很大的“单元”,那么考虑将这些私有成员从这个类中重构出来。
如果重构不合适或不可行,在进行单元测试时,是否可以使用策略模式来替换对这些私有成员函数/成员类的访问?在单元测试中,该策略将提供额外的验证,但在发布版本中,它将是简单的传递。
当我的类中有足够复杂的私有方法,以至于我觉得需要直接测试私有方法时,这是一种代码气味:我的类太复杂了。
我解决这些问题的通常方法是梳理出一个包含有趣内容的新类。通常,这个方法及其交互的字段,或者可以将另一个或两个方法提取到一个新类中。
新类将这些方法公开为“public”,因此可以对它们进行单元测试。新的和旧的类现在都比原来的类简单,这对我来说很好(我需要保持简单,否则我会迷路!)。
注意,我并不是建议人们不用大脑就可以创建课程!这里的重点是使用单元测试的力量来帮助您找到好的新类。
在使用ExpectedException时,对Cem Catikka的答案进行了快速补充:
请记住,预期的异常将被包装在InvocationTargetException中,因此为了获得异常,必须抛出收到的InvocationTargetException的原因。类似于(在BizService上测试私有方法validateRequest()):
@Rule
public ExpectedException thrown = ExpectedException.none();
@Autowired(required = true)
private BizService svc;
@Test
public void testValidateRequest() throws Exception {
thrown.expect(BizException.class);
thrown.expectMessage(expectMessage);
BizRequest request = /* Mock it, read from source - file, etc. */;
validateRequest(request);
}
private void validateRequest(BizRequest request) throws Exception {
Method method = svc.getClass().getDeclaredMethod("validateRequest", BizRequest.class);
method.setAccessible(true);
try {
method.invoke(svc, request);
}
catch (InvocationTargetException e) {
throw ((BizException)e.getCause());
}
}
从本文:使用JUnit和SuiteRunner测试私有方法(Bill Venners)中,您基本上有4个选项:
不要测试私有方法。授予方法包访问权限。使用嵌套测试类。使用反射。
您可以使用PowerMockito为要测试的私有方法中调用/使用的私有字段和私有方法设置返回值:
例如,为私有方法设置返回值:
MyClient classUnderTest = PowerMockito.spy(new MyClient());
// Set the expected return value
PowerMockito.doReturn(20).when(classUnderTest, "myPrivateMethod", anyString(), anyInt());
// This is very important. Otherwise, it will not work
classUnderTest.myPrivateMethod();
// Setting the private field value as someValue:
Whitebox.setInternalState(classUnderTest, "privateField", someValue);
最后,您可以通过以下方式验证您的私有方法:
String msg = Whitebox.invokeMethod(obj, "privateMethodToBeTested", "param1");
Assert.assertEquals(privateMsg, msg);
Or
如果classUnderTest私有方法不返回值,但它设置了另一个私有字段,则可以获取该私有字段值以查看其设置是否正确:
// To get the value of a private field
MyClass obj = Whitebox.getInternalState(classUnderTest, "foo");
assertThat(obj, is(notNull(MyClass.class))); // Or test value