深入Python3 (Dive Into Python3)笔记9--单元测试

一切内容都是摘抄,主要是便于回忆和鼓励自己不要间断,更详细内容请见原帖地址:

《深入 python3 》中文版

http://woodpecker.org.cn/diveintopython3/index.html


9.1. (不要)深入

在这里停下来。现在让我们进行一些意想不到的操作:编写一个测试用例来检测 to_roman 函数是否实现了你想要的功能。你想得没错:你正在编写测试尚未编写代码的代码。

这就是所谓的测试驱动开发TDD。那两个转换方法( to_roman() 及之后的 from_roman() )可以独立于任何使用它们的大程序而作为一个单元来被编写及测试。Python 自带一个单元测试框架,被恰当地命名为 unittest 模块。

单元测试是整个以测试为中心的开发策略中的一个重要部分。编写单元测试应该安排在项目的早期,同时要让它随同代码及需求变更一起更新。很多人都坚持测试代码应该先于被测试代码的,而这种风格也是我在本节中所主张的。但是,不管你何时编写,单元测试都是有好处的。

  • 在编写代码之前,通过编写单元测试来强迫你使用有用的方式细化你的需求。
  • 在编写代码时,单元测试可以使你避免过度编码。当所有测试用例通过时,实现的方法就完成了。
  • 重构代码时,单元测试用例有助于证明新版本的代码跟老版本功能是一致的。
  • 在维护代码期间,如果有人对你大喊:你最新的代码修改破坏了原有代码的状态,那么此时单元测试可以帮助你反驳(“先生 ,所有单元测试用例通过了我才提交代码的...”)。
  • 在团队编码中,缜密的测试套件可以降低你的代码影响别人代码的机会,这是因为你需要优先执行别人的单元测试用例。(我曾经在代码冲刺见过这种实践。一个团队把任务分解,每个人领取其中一小部分任务,同时为其编写单元测试;然后,团队相互分享他们的单元测试用例。这样,所有人都可以在编码过程中提前发现谁的代码与其他人的不可以良好工作。)

9.2. 一个简单的问题

每个测试都是一个孤岛。

一个测试用例仅回答一个关于它正在测试的代码问题。一个测试用例应该可以:

  • ……完全自动运行,而不需要人工干预。单元测试几乎是全自动的。
  • ……自主判断被测试的方法是通过还是失败,而不需要人工解释结果。
  • ……独立运行,而不依赖其它测试用例(即使测试的是同样的方法)。即,每一个测试用例都是一个孤岛。
  • 为了编写测试用例,首先使该测试用例类成为unittest 模块的TestCase 类的子类。TestCase 提供了很多你可以用于测试特定条件的测试用例的有用的方法。
  • 这里调用了真实的 to_roman() 方法. (当然,该方法还没编写;但一旦该方法被实现,这就是调用它的行号)。注意,现在你已经为 to_roman() 方法定义了 接口:它必须包含一个整型(被转换的数字)及返回一个字符串(罗马数字的表示形式)。如果 接口 实现与这些定义不一致,那么测试就会被视为失败。同样,当你调用 to_roman() 时,不要捕获任何异常。这些都是unittest 故意设计的。当你以有效的输入调用 to_roman() 时它不会抛出异常。如果 to_roman() 抛出了异常,则测试被视为失败。
  • 假设 to_roman() 方法已经被正确定义,正确调用,成功实现以及返回了一个值,那么最后一步就是去检查它的返回值是否 right 。这是测试中一个普遍的问题。TestCase 类提供了一个方法 assertEqual 来检查两个值是否相等。如果 to_roman() (result ) 的返回值跟已知的期望值g (numeral )不一致,则抛出异常,并且测试失败。如果两值相等, assertEqual 不会做任何事情。如果 to_roman() 的所有返回值均与已知的期望值一致,则 assertEqual 不会抛出任何异常,于是,test_to_roman_known_values 最终会会正常退出,这就意味着 to_roman() 通过此次测试。

编写一个失败的测试,然后进行编码直到该测试通过。

一旦你有了测试用例,你就可以开始编写 to_roman() 方法。首先,你应该用一个空方法作为存根,同时确认该测试失败。因为如果在编写任何代码之前测试已经通过,那么你的测试对你的代码是完全不会有效果的!单元测试就像跳舞:测试先行,编码跟随。编写一个失败的测试,然后进行编码直到该测试通过。

9.3. “停止然后着火”

  1. 如前一个测试用例,创建一个继承于 unittest.TestCase 的类。你可以在每个类中实现多个测试(正如你在本节中将会看到的一样),但是我却选择了创建一个新类,因为该测试与上一个有点不同。这样,我们可以把正常输入的测试跟非法输入的测试分别放入不同的两个类中。
  2. 如前一个测试用例,测试本身是类一个方法,并且该方法以 test 开头命名。
  3. unittest.TestCase 类提供e assertRaises 方法,该方法需要以下参数:你期望的异常、你要测试的方法及传入给方法的参数。(如果被测试的方法需要多个参数的话,则把所有参数依次传入 assertRaises , assertRaises 会正确地把参数传递给被测方法的。)

请关注代码的最后一行。这里并不需要直接调用 to_roman() ,同时也不需要手动检查它抛出的异常类型(通过 一个 try...except 块来包装),而这些 assertRaises 方法都给我们完成了。你要做的所有事情就是告诉assertRaises你期望的异常类型( roman2.OutOfRangeError )、被测方法(to_roman() )以及方法的参数(4000 )。assertRaises 方法负责调用 to_roman() 和检查方法抛出 roman2.OutOfRangeError 的异常。

为什么代码没有正确执行呢?回溯说明了一切。你正在测试的模块没有叫 OutOfRangeError 的异常。回忆一下,该异常是你传递给 assertRaises() 方法的,因为你期望当传递给被测试方法一个超大值时可以抛出该异常。但是,该异常并不存在,因此 assertRaises() 的调用会失败。事实上测试代码并没有机会测试 to_roman() 方法,因为它还没有到达那一步。

为了解决该问题,你需要在 roman2.py 中定义 OutOfRangeError 。

事实上,异常类可以不做任何事情,但是至少添加一行代码使其成为一个类。 pass 的真正意思是什么都不做,但是它是一行Python代码,所以可以使其成为类。

9.4. More Halting, More Fire

if not (0 < n < 4000):

这是Python优雅的快捷方法:一次性的多比较。它等价于 if not ((0 < n) and (n < 4000)),但前者更适合阅读。这一行代码应该捕获那些超大的、负值的或者为 0 的输入。

 

9.5. 还有一件事情……

9.6. 可喜的对称性

9.7. 更多错误输入

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值