基于参数化夹具的 Py.Test 参数化

al1*_*616 3 python pytest

我有一个类范围的参数化夹具,它为其参数获取 3 个数据库并返回到每个数据库的连接。

类中的测试使用此装置来测试每个数据库连接属性。

现在我有一个带有数据库表测试的新类,我想使用上面的夹具但要在每个连接表上进行参数化。

关于实现这一点的 pytest 方式有什么建议吗?我找不到基于已经参数化的元素进行参数化的方法。

谢谢

Jan*_*sky 6

测试类用于:

  • 为测试用例提供设置和拆卸功能
  • 在测试期间分享一些共同的价值观

随着pytest这是没有必要的安装和拆卸能够在灯具级的完成。

出于这个原因,我的解决方案不使用类(但它可能与它们一起使用)。

为了表明,(假)连接被创建,然后关闭观察标准输出上的输出。诀窍是使用@pytest.yield_fixture,它不是使用return而是yield提供注入测试用例的参数中使用的值。第一条yield语句之后的任何内容都作为拆卸代码执行。

“矩形”样式:M x N 测试由两个参数化装置运行

第一种情况很自然py.test,所有夹具变体都组合在一起。

因为它有 M x N 测试用例运行,我称之为“矩形”。

我的测试在tests/test_it.py

import pytest


@pytest.yield_fixture(scope="class", params=["mysql", "pgsql", "firebird"])
def db_connect(request):
    print("\nopening db")
    yield request.param
    print("closing db")


@pytest.fixture(scope="class", params=["user", "groups"])
def table_name(request):
    return request.param


def test_it(db_connect, table_name):
    print("Testing: {} + {}".format(db_connect, table_name))
Run Code Online (Sandbox Code Playgroud)

如果您需要更多类似的测试用例test_it,只需使用另一个名称创建它们。

运行我的测试用例::

$ py.test -sv tests
========================================= test session starts =========================================
platform linux2 -- Python 2.7.9 -- py-1.4.30 -- pytest-2.7.2 -- /home/javl/.virtualenvs/stack/bin/python2
rootdir: /home/javl/sandbox/stack/tests, inifile: 
collected 6 items 

tests/test_it.py::test_it[mysql-user] 
opening db
Testing: mysql + user
PASSEDclosing db

tests/test_it.py::test_it[pgsql-user] 
opening db
Testing: pgsql + user
PASSEDclosing db

tests/test_it.py::test_it[pgsql-groups] 
opening db
Testing: pgsql + groups
PASSEDclosing db

tests/test_it.py::test_it[mysql-groups] 
opening db
Testing: mysql + groups
PASSEDclosing db

tests/test_it.py::test_it[firebird-groups] 
opening db
Testing: firebird + groups
PASSEDclosing db

tests/test_it.py::test_it[firebird-user] 
opening db
Testing: firebird + user
PASSEDclosing db


====================================== 6 passed in 0.01 seconds =======================================
Run Code Online (Sandbox Code Playgroud)

从一个灯具到 N 个相关灯具的“爆炸三角形”

思路如下:

  • db_connect使用参数化夹具生成几个夹具
  • 为每个 db_connect 生成 N 个table_name夹具变体
  • test_it(db_connect, table_name)只有通过适当的combinatins被称为db_connecttable_name

这根本行不通

唯一的解决方案是使用某种场景,这些场景明确定义了哪些组合是正确的。

“场景”:测试功能级别的间接参数化

我们必须参数化测试函数,而不是参数化装置。

通常,参数值按原样直接传递给测试函数。如果我们想要一个fixture(命名为参数名称)来负责创建要使用的值,我们必须将参数指定为indirect。如果我们说indirect=True,所有参数都将被这样处理,如果我们提供参数名称列表,则只有指定的参数将被传递到fixture,其余的将按原样进入测试函数。在这里,我使用了间接参数的显式列表。

import pytest

DBCFG = {"pgsql": "postgresql://scott:tiger@localhost:5432/mydatabaser",
         "mysql": "mysql://scott:tiger@localhost/foo",
         "oracle": "oracle://scott:tiger@127.0.0.1:1521/sidname"
}


@pytest.yield_fixture(scope="session")
def db_connect(request):
    connect_name = request.param
    print("\nopening db {connect_name}".format(connect_name=connect_name))
    assert connect_name in DBCFG
    yield DBCFG[connect_name]
    print("\nclosing db {connect_name}".format(connect_name=connect_name))


@pytest.fixture(scope="session")
def table_name(request):
    return "tabname-by-fixture {request.param}".format(request=request)


scenarios = [
    ("mysql", "myslq-user"),
    ("mysql", "myslq-groups"),
    ("pgsql", "pgsql-user"),
    ("pgsql", "pgsql-groups"),
    ("oracle", "oracle-user"),
    ("oracle", "oracle-groups"),
]
@pytest.mark.parametrize("db_connect,table_name",
                         scenarios,
                         indirect=["db_connect", "table_name"])
def test_it(db_connect, table_name):
    print("Testing: {} + {}".format(db_connect, table_name))
Run Code Online (Sandbox Code Playgroud)

运行测试套件:

$ py.test -sv tests/test_indirect.py
py.test========================================= test session starts ==================================
=======
platform linux2 -- Python 2.7.9, pytest-2.8.7, py-1.4.31, pluggy-0.3.1 -- /home/javl/.virtualenvs/stack
/bin/python2
cachedir: tests/.cache
rootdir: /home/javl/sandbox/stack/tests, inifile:
collected 6 items

tests/test_indirect.py::test_it[mysql-myslq-user]
opening db mysql
Testing: mysql://scott:tiger@localhost/foo + tabname-by-fixture myslq-user
PASSED
closing db mysql

tests/test_indirect.py::test_it[mysql-myslq-groups]
opening db mysql
Testing: mysql://scott:tiger@localhost/foo + tabname-by-fixture myslq-groups
PASSED
closing db mysql

tests/test_indirect.py::test_it[pgsql-pgsql-user]
opening db pgsql
Testing: postgresql://scott:tiger@localhost:5432/mydatabaser + tabname-by-fixture pgsql-user
PASSED
closing db pgsql

tests/test_indirect.py::test_it[pgsql-pgsql-groups]
opening db pgsql
Testing: postgresql://scott:tiger@localhost:5432/mydatabaser + tabname-by-fixture pgsql-groups
PASSED
closing db pgsql

tests/test_indirect.py::test_it[oracle-oracle-user]
opening db oracle
Testing: oracle://scott:tiger@127.0.0.1:1521/sidname + tabname-by-fixture oracle-user
PASSED
closing db oracle

tests/test_indirect.py::test_it[oracle-oracle-groups]
opening db oracle
Testing: oracle://scott:tiger@127.0.0.1:1521/sidname + tabname-by-fixture oracle-groups
PASSED
closing db oracle


====================================== 6 passed in 0.01 seconds =======================================
Run Code Online (Sandbox Code Playgroud)

我们看到它有效。

无论如何,有一个小问题 -db_connect范围“会话”不受尊重,它在函数级别被实例化和销毁。这是已知问题