pytest测试框架系列 - 正确的使用参数化parametrize,让你的代码更加优雅、简洁!

前言

我们先看下如果测试用例,我们在模拟用户名和密码进行登录功能,你们觉得有什么问题呢?
示例:

# !/usr/bin/python3
# _*_coding:utf-8 _*_
""""
# @Time  :2021/7/7 21:37
# @Author  : king
# @File    :test_params.py
# @Software  :PyCharm
# @blog     :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【测试之路笔记】
"""
import pytest

# 用户正常登录
def test_login_success():
    username = "king"
    password = 123456
    assert username == "king" and password == 123456

# 用户名为空
def test_login_username_empty():
    username = ""
    password = 123456
    assert username == "king" and password == 123456

# 用户名错误
def test_login_username_error():
    username = "king1"
    password = 123456
    assert username == "king" and password == 123456

if __name__ == '__main__':
    pytest.main()

从上面例子可以看出来,存在大量重复代码,是不是看起来非常不优雅,从上面可以看出来只是username和password不一样,我们想象一下能否通过传参方式呢?

通过参数化修改上面例子:

# -*- coding: utf-8 -*-

"""
# @Time  :2021/7/7 21:37
# @Author  : king
# @File    :test_params.py
# @Software  :PyCharm
# @blog     :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【测试之路笔记】
"""
import pytest

@pytest.mark.parametrize("user,pwd", [("king", 123456), ("", 1234567), ("king1", 1234567)])
def test_login(user, pwd):
    username = user
    password = pwd
    assert username == "king" and password == 123456

if __name__ == '__main__':
    pytest.main()

通过上面参数化的修改,代码是不是看起来简单很多而且优雅了

@pytest.mark.parametrize 详解(建议掌握程度:☆☆☆☆☆)

说明:
源码分析:

    def parametrize(
        self,
        argnames: Union[str, List[str], Tuple[str, ...]],
        argvalues: Iterable[Union[ParameterSet, Sequence[object], object]],
        indirect: Union[bool, Sequence[str]] = False,
        ids: Optional[
            Union[
                Iterable[Union[None, str, float, int, bool]],
                Callable[[Any], Optional[object]],
            ]
        ] = None,
        scope: "Optional[_Scope]" = None,
        *,
        _param_mark: Optional[Mark] = None,
    ) -> None:
        """Add new invocations to the underlying test function using the list
        of argvalues for the given argnames.  Parametrization is performed
        during the collection phase.  If you need to setup expensive resources
        see about setting indirect to do it rather at test setup time.

        :param argnames:
            A comma-separated string denoting one or more argument names, or
            a list/tuple of argument strings.

        :param argvalues:
            The list of argvalues determines how often a test is invoked with
            different argument values.

            If only one argname was specified argvalues is a list of values.
            If N argnames were specified, argvalues must be a list of
            N-tuples, where each tuple-element specifies a value for its
            respective argname.

        :param indirect:
            A list of arguments' names (subset of argnames) or a boolean.
            If True the list contains all names from the argnames. Each
            argvalue corresponding to an argname in this list will
            be passed as request.param to its respective argname fixture
            function so that it can perform more expensive setups during the
            setup phase of a test rather than at collection time.

        :param ids:
            Sequence of (or generator for) ids for ``argvalues``,
            or a callable to return part of the id for each argvalue.

            With sequences (and generators like ``itertools.count()``) the
            returned ids should be of type ``string``, ``int``, ``float``,
            ``bool``, or ``None``.
            They are mapped to the corresponding index in ``argvalues``.
            ``None`` means to use the auto-generated id.

            If it is a callable it will be called for each entry in
            ``argvalues``, and the return value is used as part of the
            auto-generated id for the whole set (where parts are joined with
            dashes ("-")).
            This is useful to provide more specific ids for certain items, e.g.
            dates.  Returning ``None`` will use an auto-generated id.

            If no ids are provided they will be generated automatically from
            the argvalues.

        :param scope:
            If specified it denotes the scope of the parameters.
            The scope is used for grouping tests by parameter instances.
            It will also override any fixture-function defined scope, allowing
            to set a dynamic scope using test context or configuration.
        """
"""
翻译:
	功能说明:使用给定参数列表值为基本功能测试函数添加新调用,在收集阶段执行参数化,如果您需要设置
	高级的资源,请参阅设置indirect进行设置,而不是在测试设置时
	
	:param argnames: 一个逗号分隔的一个或多个字符串参数,或字符串的列表/元组参数。

	:param argvalues:这个参数列表决定了测试执行次数通过不同的参数值. 如果仅仅一个参数,指定参数值
	为一个列表。如果有N个参数,参数值必须为一个列表且里面为元祖,每个元祖里面的值与参数名相对应。

	:param indirect:参数名称列表(参数名称的子集)或布尔值。
	如果为 True,则列表包含 argnames 中的所有名称。 每个与此列表中的 argname 对应的 argvalue 将
	作为 request.param 传递给其各自的 argname 夹具功能,以便它可以在运行期间执行更高级的测试而
	不是收集时间
	
	 :param ids:  argvalues 的(或生成器)id 序列,或可调用以返回每个 argvalue 的 id 的一部分。
	 使用序列(以及像“itertools.count()”这样的生成器)返回的 id 应该是类型 ``string``, ``int``, 
	 ``float``,``bool`` 或 ``None``。
	 它们被映射到 argvalues 中的相应索引。``None`` 表示使用自动生成的 id。
	 如果它是可调用的,它将为中的每个条目调用``argvalues``,并且返回值用作整个集合的自动生成的 
	 id(他们之间使用破折号连接("-"))。
	 这对于为某些项目提供更具体的 id 很有用,例如日期。 返回 ``None`` 将使用自动生成的 id。
	 如果没有提供id,就使用参数值。
	
	:param scope: 如果指定,则表示参数的范围。
	范围用于按参数实例对测试进行分组。
	它还将覆盖任何夹具功能定义的范围,允许使用测试上下文或配置设置动态范围。
"""

使用方法

  • @pytest.mark.parametrize(“key”, [“val1”, “val2”, “val3”, “val4”])
  • @pytest.mark.parametrize(“key”, [{“key”: “val1”}, {“key”: “val2”}])
  • @pytest.mark.parametrize(“key1,key2”, [(“val1”, “val2”), (“val3”, “val4”)])
  • @pytest.mark.parametrize([“key1”, “key2”], [(“val1”, “val2”), (“val3”, “val4”)])
  • @pytest.mark.parametrize((“key1”, “key2”), [(“val1”, “val2”), (“val3”, “val4”)])
  • @pytest.mark.parametrize(“key1,key2”, [[{“key1”: “val1”},{“key2”: “val2”}], [{“key1”: “val2”},{“key2”: “val3”}]])
@pytest.mark.parametrize(“key”, [“val1”, “val2”, “val3”, “val4”])

示例:

# -*- coding: utf-8 -*-
"""
# @Project  :demo_test
# @Time    :2021/7/8 8:33
# @Author  :king
# @File    :test_params.py
# @blog     :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【测试之路笔记】
"""
import pytest

@pytest.mark.parametrize("user", ["king", "king1"])
def test_login(user):
    assert user.startswith("king")

if __name__ == '__main__':
    pytest.main()

执行结果:
在这里插入图片描述

@pytest.mark.parametrize(“key”, [{“key”: “val1”}, {“key”: “val2”}])

关键代码示例:

@pytest.mark.parametrize("user", [{"user": "king"}, {"user": "king1"}])
def test_login(user):
    assert user["user"].startswith("king")

其他方式大家可以按照上面示例进行使用即可

多个参数组合进行参数化

注意:多个参数组合时使用的是笛卡尔积
示例:

# -*- coding: utf-8 -*-

"""
# @Project  :demo_test
# @Time    :2021/7/8 8:33
# @Author  :king
# @File    :test_params.py
# @blog     :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【测试之路笔记】
"""
import pytest

@pytest.mark.parametrize("user", ["king", "wangyong"])
@pytest.mark.parametrize("pwd", [123456, 1234567])
def test_login(user, pwd):
    assert user == "king" and pwd == 123456

if __name__ == '__main__':
    pytest.main()

执行结果:
在这里插入图片描述

ids使用

先看下未设置ids时:

# -*- coding: utf-8 -*-

"""
# @Project  :demo_test
# @Time    :2021/7/8 8:33
# @Author  :king
# @File    :test_params.py
# @blog     :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【测试之路笔记】
"""
import pytest

@pytest.mark.parametrize("user,pwd", [("king", 123456), ("wangyong", 123456)])
def test_login(user, pwd):
    assert user.startswith("king") and pwd == 123456

if __name__ == '__main__':
    pytest.main()

执行结果为:
在这里插入图片描述

在看下设置ids时:

# -*- coding: utf-8 -*-

"""
# @Project  :demo_test
# @Time    :2021/7/8 8:33
# @Author  :king
# @File    :test_params.py
# @blog     :https://blog.csdn.net/u010454117
# @WeChat Official Account: 【测试之路笔记】
"""
import pytest

@pytest.mark.parametrize("user,pwd", [("king", 123456), ("wangyong", 123456)], 
ids=["正确用户名", "用户名错误"])
def test_login(user, pwd):
    assert user.startswith("king") and pwd == 123456

if __name__ == '__main__':
    pytest.main()

执行结果:
在这里插入图片描述

indirect、scope参数使用

备注:目前基本上不使用这两个参数,暂时不讲解,需要再加上!

总结

  • @pytest.mark.parametrize() 参数可以有3中方式,例如 "user,pwd"["user", "pwd"]("user", "pwd")
  • @pytest.mark.parametrize() 参数的值是可迭代的对象,每个迭代的值与参数个数必须保持一致
  • @pytest.mark.parametrize() 参数ids的数量与参数的值个数必须保持一致

以上为内容纯属个人理解,如有不足,欢迎各位大神指正,转载请注明出处!

如果觉得文章不错,欢迎关注微信公众号,微信公众号每天推送相关测试技术文章
个人微信号

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

测试之路king

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值