我想编写一个测试,以确定在给定的情况下不会引发异常。
测试是否引发异常是很简单的…
sInvalidPath=AlwaysSuppliesAnInvalidPath()
self.assertRaises(PathIsNotAValidOne, MyObject, sInvalidPath)
... 但你怎么能反其道而行之呢。
像这样的东西是我所追求的…
sValidPath=AlwaysSuppliesAValidPath()
self.assertNotRaises(PathIsNotAValidOne, MyObject, sValidPath)
我发现monkey-patch unittest很有用,如下所示:
def assertMayRaise(self, exception, expr):
if exception is None:
try:
expr()
except:
info = sys.exc_info()
self.fail('%s raised' % repr(info[0]))
else:
self.assertRaises(exception, expr)
unittest.TestCase.assertMayRaise = assertMayRaise
这在测试异常是否存在时阐明了意图:
self.assertMayRaise(None, does_not_raise)
这也简化了循环测试,我经常这样做:
# ValueError is raised only for op(x,x), op(y,y) and op(z,z).
for i,(a,b) in enumerate(itertools.product([x,y,z], [x,y,z])):
self.assertMayRaise(None if i%4 else ValueError, lambda: op(a, b))
只需要调用函数。如果它引发异常,单元测试框架会将其标记为错误。你可能想要添加一个评论,例如:
sValidPath=AlwaysSuppliesAValidPath()
# Check PathIsNotAValidOne not thrown
MyObject(sValidPath)
编辑以补充评论中的澄清:
Unit tests can have 3 results: Pass, Fail, Error. (Actually more if you count XPass/XFail/Skip...)
If you're testing a particular exception is not thrown, and it is thrown, then in theory that should be a Fail. But the code above makes it an Error, which is theoretically "wrong".
As a practical matter, with an Error your test runner will probably print the stack trace, which may be useful debugging the failure. With a Fail you probably won't see the stack trace.
As a practical matter, with a Fail you can mark the test as "Expected to Fail". With an Error you probably can't do that, although you can mark the test as "skip".
As a practical matter, making the test case report an Error requires additional code.
Whether the difference between "Error" and "Failure" matters depends on your processes. The way my team uses unit tests, they have to all pass. (Agile programming, with a continuous integration machine that runs all the unit tests). What actually matters to my team is "do all the unit tests pass?" (i.e. "is Jenkins green?"). So for my team, there's no practical difference between "Fail" and "Error".
Due to the advantages mentioned above (less code, seeing the stack trace), and the fact that Fail/Error are treated the same by my team, I use this approach.
You may have different requirements if you use your unit tests in a different way, especially if your processes treat "fail" and "error" differently, or if you want to be able to mark tests as "expected failure".
If you would rather have this test report an Error, use DGH's answer.
我发现monkey-patch unittest很有用,如下所示:
def assertMayRaise(self, exception, expr):
if exception is None:
try:
expr()
except:
info = sys.exc_info()
self.fail('%s raised' % repr(info[0]))
else:
self.assertRaises(exception, expr)
unittest.TestCase.assertMayRaise = assertMayRaise
这在测试异常是否存在时阐明了意图:
self.assertMayRaise(None, does_not_raise)
这也简化了循环测试,我经常这样做:
# ValueError is raised only for op(x,x), op(y,y) and op(z,z).
for i,(a,b) in enumerate(itertools.product([x,y,z], [x,y,z])):
self.assertMayRaise(None if i%4 else ValueError, lambda: op(a, b))