简介
Python 的动态特性使得测试对大多数应用程序至关重要。没有编译器来帮你找出错误。找出错误的唯一方法是运行代码,并确保你尝试了它的所有功能。
This tutorial is from open-source community. Access the source code
💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版
Python 的动态特性使得测试对大多数应用程序至关重要。没有编译器来帮你找出错误。找出错误的唯一方法是运行代码,并确保你尝试了它的所有功能。
assert
语句是程序的内部检查。如果表达式不为真,它会引发 AssertionError
异常。
assert
语句语法。
assert <表达式> [, '诊断消息']
例如。
assert isinstance(10, int), '预期为整数'
它不应用于检查用户输入(即网页表单上输入的数据等)。它的目的更多是用于内部检查和不变量(应该始终为真的条件)。
也称为契约式设计,大量使用断言是一种设计软件的方法。它规定软件设计师应为软件组件定义精确的接口规范。
例如,你可以在函数的所有输入上设置断言。
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
通过这种方式,你将测试与代码包含在同一个模块中。
优点:如果代码明显有问题,尝试导入模块时将会崩溃。
但这不推荐用于全面测试。它更像是一种基本的“冒烟测试”。该函数在任何示例上都能正常工作吗?如果不能,那么肯定有地方出问题了。
unittest
模块假设你在 simple.py
中有一些代码。
## simple.py
def add(x, y):
return x + y
现在,假设你想要测试它。在 /home/labex/project/test_simple.py
中创建一个单独的测试文件,如下所示。
## test_simple.py
import simple
import unittest
然后定义一个测试类。
## test_simple.py
import simple
import unittest
## 注意它继承自 unittest.TestCase
class TestAdd(unittest.TestCase):
...
测试类必须继承自 unittest.TestCase
。
在测试类中,你定义测试方法。
## test_simple.py
import simple
import unittest
## 注意它继承自 unittest.TestCase
class TestAdd(unittest.TestCase):
def test_simple(self):
## 用简单整数参数进行测试
r = simple.add(2, 2)
self.assertEqual(r, 5)
def test_str(self):
## 用字符串进行测试
r = simple.add('hello', 'world')
self.assertEqual(r, 'helloworld')
*重要提示:每个方法都必须以 test
开头。
unittest
unittest
提供了几个内置断言。每个断言都用于验证不同的情况。
## 断言表达式 expr 为真
self.assertTrue(expr)
## 断言 x 等于 y
self.assertEqual(x,y)
## 断言 x 不等于 y
self.assertNotEqual(x,y)
## 断言 x 近似等于 y
self.assertAlmostEqual(x,y,places)
## 断言可调用对象 callable(arg1,arg2,...) 引发异常 exc
self.assertRaises(exc, callable, arg1, arg2,...)
这并非完整列表。该模块中还有其他断言。
unittest
要运行测试,需将代码转换为脚本。
## test_simple.py
...
if __name__ == '__main__':
unittest.main()
然后在测试文件上运行 Python。
$ 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
模块有大量与测试运行器、结果收集以及测试的其他方面相关的选项。详情请查阅文档。
内置的 unittest
模块的优点是随处可用 —— 它是 Python 的一部分。然而,许多程序员也发现它相当冗长。一个流行的替代方案是 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
的命令。然后它会找到并运行所有测试。可以使用 pip install pytest
安装该模块。
pytest
的功能远不止于此示例,但如果你决定尝试一下,通常很容易上手。
在本练习中,你将探索使用 Python 的 unittest
模块的基本机制。
在之前的练习中,你编写了一个包含 Stock
类的 stock.py
文件。对于本练习,假设你正在使用为练习 7.9 编写的涉及类型化属性的代码。如果由于某种原因该代码无法正常工作,你可能需要将 Solutions/7_9
中的解决方案复制到你的工作目录中。
在一个单独的文件 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'
恭喜你!你已经完成了实验(Lab)。你可以在LabEx中练习更多实验来提升你的技能。