Python 测试要点

PythonPythonBeginner
立即练习

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 中的解决方案复制到你的工作目录中。

练习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'
✨ 查看解决方案并练习

总结

恭喜你!你已经完成了实验(Lab)。你可以在LabEx中练习更多实验来提升你的技能。