翻译:《实用的Python编程》08_01_Testing
目录 | 上一节 (7.5 装饰方法 | 下一节 (8.2 日志)
8.1 测试
多测试,少调试(Testing Rocks, Debugging Sucks)
Python 的动态性质使得测试对大多数程序而言至关重要。编译器不会发现你的 bug,发现 bug 的唯一方式是运行代码,并确保尝试了所有的特性。
断言(Assertions)
assert 语句用于程序的内部检查。如果表达式不为真,则会触发 AssertionError 异常。
assert 语句语法:
assert <expression> [, 'Diagnostic message']
示例:
assert isinstance(10, int), 'Expected int'
assert 语句不应用于检查用户的输入(例如,在网页表单输入的数据)。 assert 语句旨在用于内部检查或者用于不变量(invariant,始终为 True 的条件)。
契约式编程
契约式编程(contract programming)也称为契约式设计(Design By Contract),自由使用断言是一种软件设计方法。契约式编程规定软件设计人员应该为软件组件定义精确的接口规范。
例如,你可以在所有的函数输入中使用断言:
def add(x, y):
assert isinstance(x, int), 'Expected int'
assert isinstance(y, int), 'Expected int'
return x + y
如果函数调用者没有使用正确的参数,那么检查输入可以立即捕捉到。
>>> add(2, 3)
5
>>> add('2', '3')
Traceback (most recent call last):
...
AssertionError: Expected int
>>>
内联测试
断言也可以用于简单的测试。
def add(x, y):
return x + y
assert add(2,2) == 4
这样,你就可以将测试与代码包含在同一模块中。
好处:如果代码明显被破坏,那么尝试导入模块将会导致程序崩溃。
对于详尽的测试,不推荐这样做。这种做法更像是基本的“冒烟测试(smoke test)”。函数是否可以在所有的用例上正常工作?如果不可以,那么肯定是有问题的。
unittest 模块
假设你有下面这样一段代码:
# simple.py
def add(x, y):
return x + y
现在,你想对这些代码进行测试,请创建一个单独的测试文件,如下所示:
# test_simple.py
import simple
import unittest
然后定义一个测试类:
# test_simple.py
import simple
import unittest
# Notice that it inherits from unittest.TestCase
class TestAdd(unittest.TestCase):
...
测试类必须继承自unittest.TestCase。
在测试类中,定义测试方法:
# test_simple.py
import simple
import unittest
# Notice that it inherits from unittest.TestCase
class TestAdd(unittest.TestCase):
def test_simple(self):
# Test with simple integer arguments
r = simple.add(2, 2)
self.assertEqual(r, 5)
def test_str(self):
# Test with strings
r = simple.add('hello', 'world')
self.assertEqual(r, 'helloworld')
重要提示:每个方法的名称必须以 test 开头。
使用 unittest
unittest 中内置了一些断言,每种断言对不同的事情进行诊断。
# Assert that expr is True
self.assertTrue(expr)
# Assert that x == y
self.assertEqual(x,y)
# Assert that x != y
self.assertNotEqual(x,y)
# Assert that x is near y
self.assertAlmostEqual(x,y,places)
# Assert that callable(arg1,arg2,...) raises exc
self.assertRaises(exc, callable, arg1, arg2, ...)
上述列表并不是一个完整的列表,unittest 模块还有其它断言。
运行 unittest
要运行测试,请把代码转换为脚本。
# test_simple.py
...
if __name__ == '__main__':
unittest.main()
然后使用 Python 执行测试文件:
bash % python3 test_simple.py
F.
========================================================
FAIL: test_simple (__main__.TestAdd)
--------------------------------------------------------
Traceback (most recent call last):
File "testsimple.py", line 8, in test_simple
self.assertEqual(r, 5)
AssertionError: 4 != 5
--------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (failures=1)
说明
高效的单元测试是一种艺术。对于大型应用而言,单元测试可能会变得非常复杂。
unittest 模块具有大量与测试运行器(test runners),测试结果集(collection of results)以及测试其他方面相关的选项。相关详细信息,请查阅文档。
第三方测试工具
虽然内置 unittest 模块的优势是可以随处使用——因为它是 Python 的一部分,但是许多程序员也觉得 unittest 非常繁琐。另一个流行的的测试工具是 pytest。使用 pytest,测试文件可以简化为以下形式:
# test_simple.py
import simple
def test_simple():
assert simple.add(2,2) == 4
def test_str():
assert simple.add('hello','world') == 'helloworld'
要运行测试,只需要输入一个命令即可,例如:python -m pytest 。它将会发现所有的测试并运行这些测试。
除了这个示例之外,pytest 还有很多内容。如果你决定尝试一下,通常很容易上手。
练习
在本次练习中,我们将探索使用 Python unittest 模块的基本机制(mechanics)。
在前面的练习中,我们编写了一个包含 Stock 类的 stock.py 文件。对于本次练习,假设我们使用的是 练习7.9 中编写的与类型化属性相关的代码(译注:typedproperty.py)。如果因为某些原因,练习 7.9 的代码无法正常工作,你可以从 Solutions/7_9 中复制 typedproperty.py 到工作目录中。
练习 8.1:编写单元测试
请创建一个单独的 test_stock.py 文件,为 Stock 编写单元测试集。为了让你入门,这里有一小段测试实例创建的代码:
# test_stock.py
import unittest
import stock
class TestStock(unittest.TestCase):
def test_create(self):
s = stock.Stock('GOOG', 100, 490.1)
self.assertEqual(s.name, 'GOOG')
self.assertEqual(s.shares, 100)
self.assertEqual(s.price, 490.1)
if __name__ == '__main__':
unittest.main()
运行单元测试,你应该可以获得一些像下面这有的输出:
.
----------------------------------------------------------------------
Ran 1 tests in 0.000s
OK
然后,编写其它单元测试来检查以下各项内容:
- 确保
s.cost属性返回正确的值(49010.0)。 - 确保
s.sell()方法正常工作。它应该相应地减小s.shares。 - 确保
s.shares属性只能设置为整数值。
对于最后一部分,你需要检查异常的触发。要做到这些,一种简单的方法是使用如下代码:
class TestStock(unittest.TestCase):
...
def test_bad_shares(self):
s = stock.Stock('GOOG', 100, 490.1)
with self.assertRaises(TypeError):
s.shares = '100'
目录 | 上一节 (7.5 装饰方法 | 下一节 (8.2 日志)
注:完整翻译见 https://github.com/codists/practical-python-zh
翻译:《实用的Python编程》08_01_Testing的更多相关文章
- 翻译:《实用的Python编程》InstructorNotes
实用的 Python 编程--讲师说明 作者:戴维·比兹利(David Beazley) 概述 对于如何使用我的课程"实用的 Python 编程"进行教学的问题,本文档提供一些通用 ...
- 翻译:《实用的Python编程》README
欢迎光临 大约 25 年前,当我第一次学习 Python 时,发现 Python 竟然可以被高效地应用到各种混乱的工作项目上,我立即被震惊了.15 年前,我自己也将这种乐趣教授给别人.教学的结果就是本 ...
- 翻译:《实用的Python编程》05_02_Classes_encapsulation
目录 | 上一节 (5.1 再谈字典) | 下一节 (6 生成器) 5.2 类和封装 创建类时,通常会尝试将类的内部细节进行封装.本节介绍 Python 编程中有关封装的习惯用法(包括私有变量和私有属 ...
- 翻译:《实用的Python编程》04_02_Inheritance
目录 | 上一节 (4.1 类) | 下一节 (4.3 特殊方法) 4.2 继承 继承(inheritance)是编写可扩展程序程序的常用手段.本节对继承的思想(idea)进行探讨. 简介 继承用于特 ...
- 翻译:《实用的Python编程》01_02_Hello_world
目录 | 上一节 (1.1 Python) | 下一节 (1.3 数字) 1.2 第一个程序 本节讨论有关如何创建一个程序.运行解释器和调试的基础知识. 运行 Python Python 程序始终在解 ...
- 翻译:《实用的Python编程》03_03_Error_checking
目录 | 上一节 (3.2 深入函数) | 下一节 (3.4 模块) 3.3 错误检查 虽然前面已经介绍了异常,但本节补充一些有关错误检查和异常处理的其它细节. 程序是如何运行失败的 Python 不 ...
- 翻译:《实用的Python编程》03_04_Modules
目录 | 上一节 (3.3 错误检查) | 下一节 (3.5 主模块) 3.4 模块 本节介绍模块的概念以及如何使用跨多个文件的函数. 模块和导入 任何一个 Python 源文件都是一个模块. # f ...
- 翻译:《实用的Python编程》03_05_Main_module
目录 | 上一节 (3.4 模块) | 下一节 (3.6 设计讨论) 3.5 主模块 本节介绍主程序(主模块)的概念 主函数 在许多编程语言中,存在一个主函数或者主方法的概念. // c / c++ ...
- 翻译:《实用的Python编程》04_01_Class
目录 | 上一节 (3.6 设计讨论) | 下一节 (4.2 继承) 4.1 类 本节介绍 class 语句以及创建新对象的方式. 面向对象编程(OOP) 面向对象编程是一种将代码组织成对象集合的编程 ...
随机推荐
- Windows 10 滚动截图工具
Windows 10 滚动截图工具 Edge & Note & Clip https://www.runoob.com/docker/docker-architecture.html ...
- JS实现点击加载更多效果
适用场景:后端直接把所有的文章都给你调出来了,但是领导又让做点击加载更多效果...(宝宝心里苦啊) 点击加载更多效果: 第一个和第二个参数分别是btn和ul的DOM(必填) ...
- SPC空投价值高达310万美金,生态建设者直呼真香!
市场上面有句名言:"人赚不到自己认知以外的财富",这在数字加密上也是共通的.早在本月12日,也就是前天,NGK官方发行的第N波利好---SPC侧链代币空投已经陆续发放了,NGK以及 ...
- 人物传记:Mila Fletcher:如何勤于思考抓住关键?
Mila Fletcher于2007年毕业于耶鲁大学,她是一名真正意义上的法学博士,在校期间获得了马歇尔奖学金,毕业后曾在美国多家知名律师事务所任职,目前就职于星盟全球投资公司,专注于帮助公司和客户提 ...
- redis的两种持久化的机制,你真的了解么?
redis提供了两种持久化的机制 RDB和AOF机制 RDB(redis Database):RDB保存某一个时间点之前的快照数据. AOF(Append-Only File):指所有的命令行记录以r ...
- JavaScript高级:JavaScript面向对象,JavaScript内置对象,JavaScript BOM,JavaScript封装
知识点梳理 课堂讲义 1.JavaScript面向对象 1.1.面向对象介绍 在 Java 中我们学习过面向对象,核心思想是万物皆对象. 在 JavaScript 中同样也有面向对象.思想类似. 1. ...
- vue学习遇到的问题
1.vue脚手架的安装,解决链接:https://www.cnblogs.com/qcq0703/p/14439467.html2.2.2.0+ 的版本里,当在组件上使用 v-for 时,key 现在 ...
- setScaledContents
ui->catchPhotoLabel_607->setPixmap(QPixmap::fromImage(*m_imageCatchtDefaultPhoto_607).scaled(Q ...
- 算法 - 链表操作思想 && case
算法 - 链表操作题目套路 前面这一篇文章主要讲链表操作时候的实操解决方式,本文从本质讲解链表操作的元信息,学完后,再也不怕链表操作题目了. 1.链表的基本操作 链表的基本操作无外乎插入,删除,遍历 ...
- 后端程序员之路 57、go json
go自带json处理库,位于encoding/json,里面的test很具参考意义,特别是example_test.go json - The Go Programming Languagehttps ...