我见过太多Python开发者栽在测试上。有人觉得自己写的代码足够简单不需要测试。有人觉得写测试浪费时间不如多写几个功能。还有人写了测试但只测最顺利的那条路。结果呢?上线就出bug,半夜被叫起来修代码。
我自己也吃过这亏。几年前有个项目,代码跑了一个月没出问题。结果某天用户传入了一个带特殊字符的字符串,程序直接崩溃。从那以后我下定决心,必须把测试搞扎实。
后来我发现了一个神器叫pytest。它不是Python自带的,但用起来比标准库的unittest顺手太多。你写测试的时候不用写一大段样板代码,也不用继承什么类。直接写个函数就成。
pytest到底能帮你省多少事
拿最基础的测试举个例子。你写了个加法函数,想测试它对不对。用unittest你得这么写:
class TestAdd(TestCase):
def test_add(self):
self.assertEqual(add(1, 2), 3)
用pytest你可以直接写:
def test_add():
assert add(1, 2) == 3
看出差别了吗?pytest用Python原生的assert做断言,你不用记那些assertEqual、assertTrue之类的方法。报错信息也清楚,会直接告诉你左边是多少右边是多少。
fixture是测试的命根子
做测试最头疼的是准备数据。比如你要测个数据库操作,每测一次都得创建临时数据,测完再清理。pytest有个叫fixture的东西专门解决这个问题。
你写好一个fixture函数,pytest会自动帮你调它。测试跑完它还能自动帮你清理。看个例子:
import pytest
@pytest.fixture
def temp_user():
user = create_user('test')
yield user
delete_user(user.id)
def test_user_name(temp_user):
assert temp_user.name == 'test'
yield前面的代码是准备数据,后面的代码是清理数据。测试函数只管用,不用管善后。这让测试变得特别干净。
参数化让测试用例翻倍而不翻页
一个函数往往要测很多组数据。比如加法函数,你得测正数、负数、零、小数。手动写一堆重复的测试函数?那太蠢了。
@pytest.mark.parametrize('a,b,expected', [
(1, 2, 3),
(-1, -2, -3),
(0, 0, 0),
(0.5, 0.3, 0.8)
])
def test_add(a, b, expected):
assert add(a, b) == expected
一行参数化标注,你就能跑四组测试。哪组失败pytest会单独告诉你,不会让后面的测试停下来。
测异常和警告也很简单
有时候你得保证函数在特定情况下抛出异常。用pytest写这种测试就像说话一样自然:
def test_divide_by_zero():
with pytest.raises(ZeroDivisionError):
divide(1, 0)
你读这段代码就跟读英文句子一样。pytest还支持测警告、测性能,这些高阶功能等你需要了再去查。
写测试要养成习惯
光有好工具没用,你得真去写。我给自己定了个规矩:每个新函数必须先写测试再写代码。这叫测试驱动开发。一开始可能不习惯,但坚持两周你会发现bug率直线下降。
你也可以用更温和的方式。先写出函数,然后立刻补测试。别拖几天再补,那时候你自己都看不懂了。
不写测试的代码就像没系安全带的跑车。也许你开了很久都没事,但只要出事一次就够你受的。pytest帮你把安全带系上了,你只需要扣上它。