测试失败的函数的最佳方法是什么?或测试一个完全不受失败影响的功能?
例如;我有一个I/O Completion Port
类,如果无法正确初始化端口,则抛出构造函数。这会在初始化列表中使用Win32
CreateIoCompletionPort
函数。如果未正确设置句柄 - 非空值 - 则构造函数将抛出异常。我从未见过这个功能失败。
我很确定这个(以及我的代码中的其他函数)如果失败会表现正常,代码长度为50行,包括空格,所以我的问题是
a)是否值得测试它会抛出
b)如果值得测试,如何?
c)简单的包装类应该进行单元测试吗?
对于b)我想过覆盖CreateIoCompletionPort
并传递值。在单元测试中覆盖它并使其在传入某个值时返回0.但是由于这在构造函数中使用,因此这需要是静态的。这看起来有效吗?
答案 0 :(得分:3)
如果您在.NET中执行此操作,则可以将ExpectedException属性添加到测试中:
[Test, ExpectedException(typeof(SpecificException), "Exception's specific message")]
public void TestWhichHasException()
{
CallMethodThatThrowsSpecificException();
}
如果抛出该类型的异常并且抛出指定的消息,则测试将通过。该属性还有其他重载,包括具有InnerExceptions等。
答案 1 :(得分:2)
测试失败条件绝对是值得的,你的类在你想要的时候正确抛出异常,并且在类中正确处理异常。
如果您对传递给构造函数的对象进行操作,则可以轻松完成此操作...只需传入模拟。如果没有,我倾向于将功能移到受保护的方法,并覆盖受保护的方法以唤起我的失败案例。我将使用Java作为示例,但它应该很容易将想法移植到C#案例中:
public class MyClass {
public MyClass() throws MyClassException {
// Whatever, including a call to invokeCreateIoCompletionPort
}
protected int invokeCreateIoCompletionPort(String str, int i) {
return StaticClass.createIoCompletionPort(str, i);
}
}
public class MyTest {
public void myTest() {
try {
new MyClass();
fail("MyClassException was not thrown!");
} catch (MyClassException e) {
}
}
private static class MyClassWrapper extends MyClass {
@Override
protected int invokeCreateIoCompletionPort(String str, int i) {
throw new ExpectedException();
}
}
}
正如您所看到的,测试是否正在测试的构造函数或方法抛出异常非常容易,并且从外部类中注入异常也很容易引发异常。对不起,我没有使用你的实际方法,我只是用这个名字来说明它是如何使用它的,以及我如何测试你想要测试的情况。
基本上,您公开的任何API详细信息通常都可以进行测试,如果您想知道异常情况可以正常工作,您可能需要对其进行测试。
答案 2 :(得分:1)
您应该考虑以可以模拟I / O完成端口的方式编写代码。创建一个接口/抽象类,公开I / O对象所需的方法,并编写和测试执行它应该做的事情(以及可能模拟失败的选项)。
AFAIK在单元测试时模拟外部资源是一种常见做法,以最大限度地减少依赖性。
答案 3 :(得分:0)
听起来像C ++对我来说。你需要一个接缝来模拟Win32函数。例如。在您的类中,您将创建一个受保护的方法CreateIoCompletionPort()
,它调用::CreateIoCompletionPort()
并为您的测试创建一个派生自I / O完成端口类的类,并覆盖CreateIoCompletionPort()
除了NULL
之外什么也不做返回CreateIoCompletionPort()
。您的生产类仍然表现得像设计的那样,但您现在能够模拟{{1}}函数中的失败。
这项技术来自Michael Feathers的书“使用遗留代码有效地工作”。