测试失败的函数

时间:2008-08-11 08:36:39

标签: c++ unit-testing tdd

测试失败的函数的最佳方法是什么?或测试一个完全不受失败影响的功能?

例如;我有一个I/O Completion Port类,如果无法正确初始化端口,则抛出构造函数。这会在初始化列表中使用Win32 CreateIoCompletionPort函数。如果未正确设置句柄 - 非空值 - 则构造函数将抛出异常。我从未见过这个功能失败。

我很确定这个(以及我的代码中的其他函数)如果失败会表现正常,代码长度为50行,包括空格,所以我的问题是

a)是否值得测试它会抛出 b)如果值得测试,如何?
c)简单的包装类应该进行单元测试吗?

对于b)我想过覆盖CreateIoCompletionPort并传递值。在单元测试中覆盖它并使其在传入某个值时返回0.但是由于这在构造函数中使用,因此这需要是静态的。这看起来有效吗?

4 个答案:

答案 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的书“使用遗留代码有效地工作”。