测试驱动开发(TDD)是本世纪以来在软件开发领域出现的一项最重要的技术。TDD的一个非常引人注目的地方,是对单元测试的几近狂热的关注。
单元测试用来对一小段代码(通常是一个函数或方法)进行自动化的测试。Python提供了用于单元测试的PyUnit API。对于NumPy使用者,还可以利用numpy.testing模块提供的各种便捷函数(convenience function)。正如它的名字所示,numpy.testing模块就是专门用来做测试的。
具体步骤
让我们先编写一段代码,把它作为测试对象。
- 编写阶乘函数。
编写如下的阶乘函数。
def factorial(n):
if n == 0:
return 1
if n < 0:
raise ValueError, "Don't be so negative"
return numpy.arange(1, n+1).cumprod()
计算阶乘的代码和上一攻略相同,但增加了对边界条件的检查。
- 编写单元测试。
下面将编写单元测试。也许你已经发现,本书没有多少和编写类有关的内容。对于大多数攻略而言,看似没有必要这样做。
让我们做个改变,编写一个类。该类将包含单元测试的内容。该类继承自unittest模块中的TestCase
类,而unittest模块是Python标准库的一个组成部分。我们用下列参数对阶乘函数的调用过程进行测试。
- 正整数,所谓的愉快路径(happy path)
- 边界条件0
- 负整数,将引起错误
class FactorialTest(unittest.TestCase):
def test_factorial(self):
#对3的阶乘进行测试,应该能通过。
self.assertEqual(6, factorial(3)[-1])
numpy.testing.assert_equal(numpy.array([1, 2, 6]), factorial(3))
def test_zero(self):
#对0的阶乘进行测试,应该能通过。
self.assertEqual(1, factorial(0))
def test_negative(self):
#对负整数的阶乘进行测试,应该不能通过。
#阶乘函数会抛出一个ValueError类型的异常,
#但我们期望得到一个IndexError类型的异常。
self.assertRaises(IndexError, factorial(-10))
求阶乘并对其进行单元测试的完整代码如下。
import numpy
import unittest
def factorial(n):
if n == 0:
return 1
if n < 0:
raise ValueError, "Don't be so negative"
return numpy.arange(1, n+1).cumprod()
class FactorialTest(unittest.TestCase):
def test_factorial(self):
#对3的阶乘进行测试,应该能通过。
self.assertEqual(6, factorial(3)[-1])
numpy.testing.assert_equal(numpy.array([1, 2, 6]), factorial(3))
def test_zero(self):
#对0的阶乘进行测试,应该能通过。
self.assertEqual(1, factorial(0))
def test_negative(self):
#对负整数的阶乘进行测试,应该不能通过。
# 阶乘函数会抛出一个ValueError类型的异常,
# 但我们期望得到一个IndexError类型的异常。
self.assertRaises(IndexError, factorial(-10))
if __name__ == '__main__':
unittest.main()
从下面的运行结果可以看出,对负整数求阶乘的测试没有通过。
.E.
======================================================================
ERROR: test_negative (__main__.FactorialTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "unit_test.py", line 26, in test_negative
self.assertRaises(IndexError, factorial(-10))
File "unit_test.py", line 9, in factorial
raise ValueError, "Don't be so negative"
ValueError: Don't be so negative
----------------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (errors=1)
攻略小结
我们介绍了怎样使用Python标准库中的unittest模块实现简单的单元测试。我们编写了一个测试类,该类继承自unittest模块中的TestCase
类。如下的函数被用来做各种测试。
函数 | 功能描述 |
---|---|
numpy.testing.assert_equal |
测试两个NumPy数组是否相等 |
unittest.assertEqual |
测试两个值是否相等 |
unittest.assertRaises |
测试是否有某个异常被抛出 |
NumPy的testing包中包括了若干测试函数,我们应该了解其功能。
函数 | 功能描述 |
---|---|
assert_almost_equal |
如果两个数不能在给定精度的条件下近似相等,则引发一个异常 |
assert_approx_equal |
如果两个数不能在给定有效数字位数的条件下近似相等,则引发一个异常 |
assert_array_almost_equal |
如果两个数组不能在给定精度的条件下近似相等,则引发一个异常 |
assert_array_equal |
如果两个数组不相等,则引发一个异常 |
assert_array_less |
如果两个数组的形状不同,或者不满足“第一个数组中的每个元素都比第二个数组中的对应元素小”,则引发一个异常 |
assert_raises |
用规定的参数调用一个callable 对象时,如果没有引发指定类型的异常,就认为测试没通过 |
assert_warns |
如果指定类型的警告没有被抛出,就认为测试没通过 |
assert_string_equal |
断言两个字符串相等 |