1

What is the best way of testing a function that throws on failure? Or testing a function that is fairly immune to failure?

For instance; I have a I/O Completion Port class that throws in the constructor if it can't initialise the port correctly. This uses the Win32 function of CreateIoCompletionPort in the initialiser list. If the handle isn't set correctly - a non-null value - then the constructor will throw an exception. I have never seen this function fail.

I am pretty certain that this (and other functions like it in my code) if they fail will behave correctly, the code is 50 lines long including white-space, so my questions are

a) is it worth testing that it will throw
b) and if it is worth testing, how to?
c) should simple wrapper classes as these be unit-tested?

For b) I thought about overriding CreateIoCompletionPort and passing the values through. In the unit test override it and cause it to return 0 when a certain value is passed in. However since this is used in the constructor then this needs to be static. Does this seem valid or not?

4

4 回答 4

3

If you are doing this in .NET, there is an ExpectedException attribute that you can add to your test:

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

Test will pass if the exception of that type and with the specified message is thrown. The attribute has other overloads including having InnerExceptions, etc.

于 2008-08-11T09:30:32.643 回答
2

It is definitely worthwhile to test failure conditions, both that your class properly throws an exception when you want it to and that exceptions are handled properly in the class.

This can easily be done if you are acting on an object passed in to the constructor... just pass in a mock. If not, I tend to prefer to have the functionality moved to a protected method, and override the protected method to evoke my failure case. I will use Java as an example, but it should be easy enough to port the ideas to a C# case:

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();
        }
    }
}

As you can see, it is pretty easy to test whether an exception is being thrown by the constructor or method you are testing, and it is also pretty easy to inject an exception from an external class that can throw an exception. Sorry I'm not using your actual method, I just used the name to illustrate how it sounded like you are using it, and how I would test the cases it sounded you wanted to test.

Basically, any API details you expose can usually be tested, and if you want to KNOW that exceptional cases work as they should, you probably will want to test it.

于 2008-08-11T08:57:26.097 回答
1

You should consider writing your code in such a way that you can mock your I/O completion port. Make an interface/abstract class that exposes the methods you need on the I/O object, and write and test implementation that does things like it's supposed to (and an option to simulate failure perhaps).

AFAIK it's a common practice to mock external resources when unit testing, to minimize dependencies.

于 2008-08-11T08:57:54.270 回答
0

对我来说听起来像 C++。您需要一个接缝来模拟 Win32 函数。例如,在您的类中,您将创建一个受保护的方法CreateIoCompletionPort(),该方法调用::CreateIoCompletionPort()并为您的测试创建一个从您的 I/O Completion Port 类派生的类,并覆盖CreateIoCompletionPort()除了 return 之外什么都不做NULL。您的生产类的行为仍与设计时一样,但您现在可以模拟CreateIoCompletionPort()函数中的故障。

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

于 2012-09-18T13:00:41.670 回答