【问题标题】:Testing a function that throws on failure测试一个抛出失败的函数
【发布时间】:2010-09-05 16:14:50
【问题描述】:

测试抛出失败的函数的最佳方法是什么?或者测试一个完全不会失败的函数?

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

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

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

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

【问题讨论】:

    标签: c++ unit-testing tdd


    【解决方案1】:

    测试失败条件是绝对值得的,你的类在你想要的时候正确地抛出一个异常,并且异常在类中得到了正确的处理。

    如果您正在对传入构造函数的对象进行操作,则可以轻松完成...只需传入一个模拟。如果不是,我倾向于将功能移至受保护的方法,并覆盖受保护的方法以引发我的失败案例。我将以 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】:

      您应该考虑以可以模拟 I/O 完成端口的方式编写代码。创建一个接口/抽象类,在 I/O 对象上公开您需要的方法,并编写和测试执行它应该做的事情(可能还有一个模拟失败的选项)。

      AFAIK 在单元测试时模拟外部资源是一种常见的做法,以最大限度地减少依赖关系。

      【讨论】:

        【解决方案3】:

        如果您在 .NET 中执行此操作,则可以将一个 ExpectedException 属性添加到您的测试中:

        [Test, ExpectedException(typeof(SpecificException), "Exception's specific message")]
        public void TestWhichHasException()
        {
            CallMethodThatThrowsSpecificException();
        }
        

        如果抛出该类型的异常并带有指定的消息,则测试将通过。该属性还有其他重载,包括 InnerExceptions 等。

        【讨论】:

          【解决方案4】:

          对我来说听起来像 C++。您需要一个接缝来模拟 Win32 函数。例如。在您的类中,您将创建一个受保护的方法CreateIoCompletionPort(),它调用::CreateIoCompletionPort(),并为您的测试创建一个派生自您的I/O 完成端口类并覆盖CreateIoCompletionPort() 的类,只返回NULL。您的生产类仍然按照设计的方式运行,但您现在可以模拟 CreateIoCompletionPort() 函数中的故障。

          这项技术来自 Michael Feathers 的书“有效地使用遗留代码”。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-11-21
            • 2020-03-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多