Python单元测试探讨

28 Aug 2020

内部培训大纲

测试意义

测试工具

assert

assert 1==1
assert 1==1, "custom assert error message"

理解集合比较

assert {1, 2, 3} == {3, 2, 1}
assert {"a": 1, "b": 2} == {"b": 2, "a": 1}

理解 == / is 区别

正式代码禁止用assert

doctest

def sum(a, b):
    """
    >>> sum(1+2)
    3
    """

unittest

xUnit for python

TestCase / 测试用例

断言: self.assertXXX

fixture: setUp/tearDown

(条件)跳过测试

TestSuite / 测试集管理

mock

在一定作用域里面替换掉某些部分的实现逻辑

NOTE: patch到正确的位置:

cat path/to/a.py
import requests
from requests import post

def func(url):
    requests.get(url)
    post(url)

cat path/to/a_test.py

@mock.patch("requests.get", MockGetFunc)
@mock.patch("path.to.a.post", MockPostFunc)
def test_func():
    ...

原则:

mock外部交互

pytest

demo time: pytest使用, 运行参数及pytest.ini配置说明

mark

测试标签用途pytest -m

fixure

conftest.py 配置: 基于目录层级, 而非对象继承

pytest插件说明

prefer pytest over unittest

# DONT
self.assertXXX
# DO
assert ...

# DONT
with self.assertRaises(...):
    ...
# DO
with pytest.raise(...):
    ...

# DONT
if XXX:
    self.skipTest
# DONT
@unittest.skipIf(condition, reason)

# DO
@pytest.mark.skipif(condition, reason)
# DO (in DJ)
@require(service_dsn)

# DONT
@unittest.expectedFailure
# DO
@pytest.mark.xfail(...)

避免使用unittest, 尽量使用pytest理由:

coverage

原理

结果写 .coverage SQLite数据库, 依赖py代码做后续报告分析

基于stmt/语句统计, 小于代码行数: 格式化多行, 空行/注释不纳入统计

和测试无关, 可以线上程序coverage跑覆盖率, 找dead code

分目录测试并看覆盖: pytest –cov path path

ref: [.coveragerc](https://coverage.readthedocs.io/en/v4.5.x/config.html]

demo time

未覆盖代码的意义:

覆盖率 != 正确性/代码质量, 单纯加载全部全py文件都能有不错的覆盖率, 需要有正确的断言逻辑

不必追求100%测试率, 视业务严肃性决定, 完美vs及时交付的平衡, 缺陷成本意识

避免只有测试中才访问到的功能, 简化维护敞口 (参考项目vulture配置).

如何写测试

难以测试的代码 = 糟糕的代码结构

良好测试代码的评价标准: 修改代码行数 / 需要修改的测试代码行数

测试边界问题 -> 代码模块/边界识别

FIRST原则和对应实践

相关思潮

我要开始装逼了

更多测试名词轰炸:

HOME