前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >善用 pytest fixture factory 构建结构优秀的单元测试

善用 pytest fixture factory 构建结构优秀的单元测试

作者头像
布鲁斯鱼
发布2022-11-02 14:04:47
4850
发布2022-11-02 14:04:47
举报
文章被收录于专栏:布鲁斯鱼的妙想天开

Why pytest ?

评价单元测试是否优秀,分支覆盖率是非常重要的指标,而覆盖率的决定因素除了开发自身的素质以外,足够低的用例构建成本也是必不可少。

对于 Python 应用,当项目逻辑复杂度较高时,单纯使用原生的 unittest 或者是 Django 提供的 TestCase 都会遇到一个恼人的问题:测试代码大部分工作在构建各种用例。幸好有 pytest 提供的 fixture 机制,可以较好的解决这个问题。

本文简单阐述个人的 pytest 使用实践,而关于 fixture 到底好在哪里或者具体使用方法,已经有比较多的文章做了更详细的介绍,我这里就不再赘述:

理想的单元测试

从理论来讲,对于一个测试项,我们应该只需关心测试内容的输入和输出(或异常),并且最好能够放到一起管理维护,更形象地说,应该是下面这种伪代码的感觉

代码语言:javascript
复制
(输入, 预期输出)
(输入, 预期输出)
(输入, 预期输出)
(输入, 预期异常)
def test_some_content(输入, 预期输出):
	if 正常:
		assert some_process(输入) == 预期输出
	else:
		with should_raise(预期异常):
			some_process(输入)

一个普通的 pytest 使用用例:

代码语言:javascript
复制
@pytest.fixture
def user():
    return User(name="Chris", hair_color=Color("brown"))

@pytest.fixture(autouse=True)
def set_user(client, user):
    client.set_user(user)

def test_set_user(client, user):
    # client.get_user() returns another User object
    assert client.get_user() == user

可以看出 pytest.fixture 固然能够足够强大,但是并不能完全解决问题:因为 fixture 在这里的用法是 静态 的,而对于我们理想的状态,用例中的输入输出,都应该是动态指定的。

一个简单的实践

所以,我们需要利用 fixture factory 来实现

代码语言:javascript
复制
# fixture 依旧保留了复用性
@pytest.fixture
def make_fake_resp():
    def _make_fake_resp(input: str):
        def _wrapper(*args, **kwargs):
            return {"data": input}

        return _wrapper

    return _make_fake_resp

class TestUtils:
    @pytest.mark.parametrize(
        "input, expected",
        [
            # 正常情况
            ("abc", "xyz"),
						# 异常情况,当然也可以多拆分一个测试项
            ("abc", ValueError),
        ],
    )
    def test_some_content(self, input, expected, make_fake_resp):
        """测试某些内容"""

        with mock.patch('some-need-patching-refrences') as _patch:
						# 利用 fixture factory 动态生成测试用例
            _patch.side_effect = make_fake_resp(input)

            if type(expected) is type and issubclass(expected, Exception):
                with pytest.raises(expected):
										# 主体测试逻辑
                    call_test(input)
            else:
								# 主体测试逻辑
                assert call_test(input) == expected

这样一来,只要我在一开始构建好相关的工厂函数,添加更多的测试用例就只需在 parametrize 中不断添加即可,开发注意力成功从重复的工具构建转移到了真实用例。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021 年 1 月 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 理想的单元测试
  • 一个简单的实践
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档