Pytest のパラメータ化 - あなたの知らないスキル

目次

序文

テストクラスを装飾する

出力

説明する

テスト関数を装飾する

単一データ

出力

説明する

データセット

出力

説明する

グラフィカルな対応

結合データ

出力

説明する

マークの使用例

出力

説明する

ネストされた辞書

出力

可読性を高める

ids パラメータを使用する

出力

説明する

識別用のカスタム ID

出力

説明する

要約する

要約:


序文

Unittest 単体テスト フレームワークはデータ駆動型テストに DDT を使用します。では、より強力で柔軟な Pytest フレームワークにはデータ駆動型の概念がないはずがありません。実際、Pytest は @pytest.mark.parametrize デコレーターを使用してデータ駆動型テストを実装しているため、今日はデータ駆動型テストがどのように実行されるかについて簡単に説明します。

テストクラスを装飾する

"""
------------------------------------
@Time : 2019/7/25 19:18
@Auth : linux超
@File : test_parametrize.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : [email protected]
@GROUP: 878565760
------------------------------------
"""
import pytest

data_1 = [
    (1, 2, 3),
    (4, 5, 9)
]


def add(a, b):
    return a + b


@pytest.mark.parametrize('a, b, expect', data_1)
class TestParametrize(object):

    def test_parametrize_1(self, a, b, expect):
        print('\n测试函数1测试数据为\n{}-{}'.format(a, b))
        assert add(a, b) == expect

    def test_parametrize_2(self, a, b, expect):
        print('\n测试函数2数据为\n{}-{}'.format(a, b))
        assert add(a, b) == expect


if __name__ == '__main__':
    pytest.main(['-sv'])

出力

collecting ... collected 4 items

test_parametrize.py::TestParametrize::test_parametrize_1[1-2-3] 
测试函数1测试数据为
1-2
PASSED
test_parametrize.py::TestParametrize::test_parametrize_1[4-5-9] 
测试函数1测试数据为
4-5
PASSED
test_parametrize.py::TestParametrize::test_parametrize_2[1-2-3] 
测试函数2数据为
1-2
PASSED
test_parametrize.py::TestParametrize::test_parametrize_2[4-5-9] 
测试函数2数据为
4-5
PASSED

========================== 4 passed in 0.21 seconds ===========================

Process finished with exit code 0

説明する

デコレーターがテスト クラスをデコレートすると、データ コレクションがクラスのすべてのメソッドに渡されます。

テスト関数を装飾する

単一データ

"""
------------------------------------
@Time : 2019/7/25 19:18
@Auth : linux超
@File : test_parametrize.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : [email protected]
@GROUP: 878565760
------------------------------------
"""
import pytest

data = [1, 2]


@pytest.mark.parametrize('a', data)
def test_parametrize(a):
    print('\n被加载测试数据为\n{}'.format(a))


if __name__ == '__main__':
    pytest.main(['-s'])

出力

============================= test session starts =============================
platform win32 -- Python 3.7.2, pytest-4.3.1, py-1.8.0, pluggy-0.9.0
rootdir: E:\CnblogCode\pytest_parametrize, inifile:
plugins: rerunfailures-7.0, metadata-1.8.0, html-1.20.0
collected 2 items

test_parametrize.py 
被加载测试数据为
1
.
被加载测试数据为
2
.

========================== 2 passed in 0.16 seconds ===========================

Process finished with exit code 0

説明する

テスト ケースにパラメーターが 1 つだけ必要な場合は、データのリストの順序なしのネストされたシーケンスを保存します。また、 @pytest.mark.parametrize('a', data) デコレーターの最初のパラメーターも、それぞれを受け取るための変数が 1 つだけ必要になります。リスト要素の場合、2 番目のパラメーターは保存されたデータのリストを渡します。その後、テスト ケースはテスト データ (インスタンス内の a) を受け取るために同じ名前の文字列を使用する必要があり、リスト内の要素と同じ数のテスト ケースが実行されます。生成されて実行される

データセット

"""
------------------------------------
@Time : 2019/7/25 19:18
@Auth : linux超
@File : test_parametrize.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : [email protected]
@GROUP: 878565760
------------------------------------
"""
import pytest

data = [
    [1, 2, 3],
    [4, 5, 9]
]  # 列表嵌套列表
# data_tuple = [
#     (1, 2, 3),
#     (4, 5, 9)
# ]  # 列表嵌套元组


@pytest.mark.parametrize('a, b, expect', data)
def test_parametrize_1(a, b, expect):  # 一个参数接收一个数据
    print('\n测试数据为\n{},{},{}'.format(a, b, expect))
    actual = a + b
    assert actual == expect


@pytest.mark.parametrize('value', data)
def test_parametrize_2(value):  # 一个参数接收一组数据
    print('\n测试数据为\n{}'.format(value))
    actual = value[0] + value[1]
    assert actual == value[2]


if __name__ == '__main__':
    pytest.main(['-s'])

出力

============================= test session starts =============================
platform win32 -- Python 3.7.2, pytest-4.3.1, py-1.8.0, pluggy-0.9.0
rootdir: E:\CnblogCode\pytest_parametrize, inifile:
plugins: rerunfailures-7.0, metadata-1.8.0, html-1.20.0
collected 4 items

test_parametrize.py 
测试数据为
1,2,3
.
测试数据为
4,5,9
.
测试数据为
[1, 2, 3]
.
测试数据为
[4, 5, 9]
.

========================== 4 passed in 0.17 seconds ===========================

Process finished with exit code 0

説明する

テスト ケースに複数のデータが必要な場合、ネストされたシーケンスのリスト (ネストされたタプルとネストされたリスト) を使用してテスト データを保存できます。

デコレーター @pytest.mark.parametrize() は、単一の変数または複数の変数を使用してデータを受け取ることができます。同様に、テスト ケース関数もそれと一貫性がある必要があります。

単一の変数を使用して受信する場合、テストデータがテスト関数に渡されるとき、それはリスト内の各要素または小さなリストであり、各データはインデックスを作成して取得する必要があります

複数の変数を使用してデータを受信する場合、各変数は小さなリストまたはタプルの各要素を個別に受信します。

リスト内にネストされている小さなリストまたはタプルのセットの数、および生成されるテスト ケースの数

グラフィカルな対応

結合データ

"""
------------------------------------
@Time : 2019/7/25 19:18
@Auth : linux超
@File : test_parametrize.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : [email protected]
@GROUP: 878565760
------------------------------------
"""
import pytest

data_1 = [1, 2]  
data_2 = [3, 4]  


@pytest.mark.parametrize('a', data_1)
@pytest.mark.parametrize('b', data_2)
def test_parametrize_1(a, b):
    print('\n测试数据为\n{},{}'.format(a, b))


if __name__ == '__main__':
    pytest.main(['-s'])

出力

============================= test session starts =============================
platform win32 -- Python 3.7.2, pytest-4.3.1, py-1.8.0, pluggy-0.9.0
rootdir: E:\CnblogCode\pytest_parametrize, inifile:
plugins: rerunfailures-7.0, metadata-1.8.0, html-1.20.0
collected 4 items

test_parametrize.py 
测试数据为
1,3
.
测试数据为
2,3
.
测试数据为
1,4
.
测试数据为
2,4
.

========================== 4 passed in 0.24 seconds ===========================

Process finished with exit code 0

説明する

テスト結果から、テスト関数は複数のパラメータ化されたデコレータによって同時に修飾することもできることを分析するのは難しくありません。その後、複数のデコレータのデータが相互結合されてテスト関数に渡され、n が生成されます。 *n テストのユースケース。テスト設計にも便利です。

マークの使用例

テスト ケースを直接マークでき、パラメータ化されたデコレータも認識できます (テスト ケースを失敗またはスキップとしてマーク)

無条件にスキップとしてマークされます (失敗の場合は xfail としてマークされます。自分で試してください)

"""
------------------------------------
@Time : 2019/7/25 19:18
@Auth : linux超
@File : test_parametrize.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : [email protected]
@GROUP: 878565760
------------------------------------
"""
import pytest

data_1 = [
    [1, 2, 3],
    pytest.param(3, 4, 8, marks=pytest.mark.skip)
]


def add(a, b):
    return a + b


@pytest.mark.parametrize('a, b, expect', data_1)
def test_parametrize_1(a, b, expect):
    print('\n测试数据为\n{},{}'.format(a, b))
    assert add(a, b) == expect


if __name__ == '__main__':
    pytest.main(['-vs'])

出力

============================= test session starts =============================
platform win32 -- Python 3.7.2, pytest-4.3.1, py-1.8.0, pluggy-0.9.0 -- C:\Programs\Python\Python37-32\python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.7.2', 'Platform': 'Windows-7-6.1.7601-SP1', 'Packages': {'pytest': '4.3.1', 'py': '1.8.0', 'pluggy': '0.9.0'}, 'Plugins': {'rerunfailures': '7.0', 'metadata': '1.8.0', 'html': '1.20.0'}, 'JAVA_HOME': 'D:\\JDK'}
rootdir: E:\CnblogCode\pytest_parametrize, inifile:
plugins: rerunfailures-7.0, metadata-1.8.0, html-1.20.0
collecting ... collected 2 items

test_parametrize.py::test_parametrize_1[1-2-3] 
测试数据为
1,2
PASSED
test_parametrize.py::test_parametrize_1[3-4-8] SKIPPED

===================== 1 passed, 1 skipped in 0.17 seconds =====================

Process finished with exit code 0

説明する

出力には、2 つのユース ケースが収集されたことが示されています。1 つは合格し、もう 1 つはスキップされました。特定のテスト データ セットを実行したくない場合は、skip または Skipif をマークできます。特定のデータ セットが失敗することが予想される場合は、それを xfail wait としてマークすることができます

ネストされた辞書

"""
------------------------------------
@Time : 2019/7/25 19:18
@Auth : linux超
@File : test_parametrize.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : [email protected]
@GROUP: 878565760
------------------------------------
"""
import pytest

data_1 = (
    {
        'user': 1,
        'pwd': 2
     },
    {
        'user': 3,
        'pwd': 4
    }
)


@pytest.mark.parametrize('dic', data_1)
def test_parametrize_1(dic):
    print('\n测试数据为\n{}'.format(dic))


if __name__ == '__main__':
    pytest.main(['-s'])

出力

============================= test session starts =============================
platform win32 -- Python 3.7.2, pytest-4.3.1, py-1.8.0, pluggy-0.9.0
rootdir: E:\CnblogCode\pytest_parametrize, inifile:
plugins: rerunfailures-7.0, metadata-1.8.0, html-1.20.0
collected 2 items

test_parametrize.py 
测试数据为
{'user': 1, 'pwd': 2}
.
测试数据为
{'user': 3, 'pwd': 4}
.

========================== 2 passed in 0.20 seconds ===========================

Process finished with exit code 0

可読性を高める

ids パラメータを使用する

パラメータ化されたデコレータには追加のパラメータ ID があり、各テスト ケースを識別し、テスト データ結果の表示をカスタマイズできます。読みやすさを高めるために、各テスト ケースでどのテスト データが使用されているかをマークし、適切に説明するものを追加できます。

これを使用する前に、ids パラメーターは文字列のリストである必要があり、データ オブジェクト リストの長さと一致する必要があることを知っておく必要があります。ids を使用して効果を確認してみることができます。

"""
------------------------------------
@Time : 2019/7/25 19:18
@Auth : linux超
@File : test_parametrize.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : [email protected]
@GROUP: 878565760
------------------------------------
"""
import pytest

data_1 = [
    (1, 2, 3),
    (4, 5, 9)
]


ids = ["a:{} + b:{} = expect:{}".format(a, b, expect) for a, b, expect in data_1]

def add(a, b):
    return a + b


@pytest.mark.parametrize('a, b, expect', data_1, ids=ids)  
class TestParametrize(object):

    def test_parametrize_1(self, a, b, expect):
        print('\n测试函数1测试数据为\n{}-{}'.format(a, b))
        assert add(a, b) == expect

    def test_parametrize_2(self, a, b, expect):
        print('\n测试函数2数据为\n{}-{}'.format(a, b))
        assert add(a, b) == expect


if __name__ == '__main__':
    pytest.main(['-v'])  # -v : 更加详细的输出测试结果

出力

デコレータは ids パラメータの出力を渡しません

collecting ... collected 4 items

test_parametrize.py::TestParametrize::test_parametrize_1[1-2-3] PASSED   [ 25%]
test_parametrize.py::TestParametrize::test_parametrize_1[4-5-9] PASSED   [ 50%]
test_parametrize.py::TestParametrize::test_parametrize_2[1-2-3] PASSED   [ 75%]
test_parametrize.py::TestParametrize::test_parametrize_2[4-5-9] PASSED   [100%]

========================== 4 passed in 0.16 seconds ===========================

Process finished with exit code 0

デコレータは ids パラメータの出力を渡します

collecting ... collected 4 items

test_parametrize.py::TestParametrize::test_parametrize_1[a:1 + b:2 = expect:3] PASSED [ 25%]
test_parametrize.py::TestParametrize::test_parametrize_1[a:4 + b:5 = expect:9] PASSED [ 50%]
test_parametrize.py::TestParametrize::test_parametrize_2[a:1 + b:2 = expect:3] PASSED [ 75%]
test_parametrize.py::TestParametrize::test_parametrize_2[a:4 + b:5 = expect:9] PASSED [100%]

========================== 4 passed in 0.20 seconds ===========================

Process finished with exit code 0

説明する

-v を使用してコマンドを実行すると、出力結果がより詳細に表示されます。データ結果のすべてのユースケースがリストで明確にマークされており、このマークを通じて、より直感的に確認できることがわかります。各テスト ユースケースで使用するデータ名とテスト内容

識別用のカスタム ID

ids パラメータを使用して出力を読みやすくすることに加えて、識別のためにパラメータ リスト内のパラメータの横に id 値を定義することもできます。次の例を参照してください。

"""
------------------------------------
@Time : 2019/7/25 19:18
@Auth : linux超
@File : test_parametrize.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : [email protected]
@GROUP: 878565760
------------------------------------
"""
import pytest

data_1 = [
    pytest.param(1, 2, 3, id="(a+b):pass"),  # id的值可以自定义, 只要方便理解每个用例是干什么的即可
    pytest.param(4, 5, 10, id="(a+b):fail")
]


def add(a, b):
    return a + b


class TestParametrize(object):

    @pytest.mark.parametrize('a, b, expect', data_1)
    def test_parametrize_1(self, a, b, expect):
        assert add(a, b) == expect


if __name__ == '__main__':
    pytest.main(['-v'])

出力

test_parametrize.py::TestParametrize::test_parametrize_1[(a+b):pass] PASSED [ 50%]
test_parametrize.py::TestParametrize::test_parametrize_1[(a+b):fail] FAILED [100%]

================================== FAILURES ===================================
_______________ TestParametrize.test_parametrize_1[(a+b):fail] ________________

self = <pytest_parametrize.test_parametrize.TestParametrize object at 0x000001D7BFC4C748>
a = 4, b = 5, expect = 10

    @pytest.mark.parametrize('a, b, expect', data_1)
    def test_parametrize_1(self, a, b, expect):
>       assert add(a, b) == expect
E       assert 9 == 10
E         -9
E         +10

test_parametrize.py:28: AssertionError
===================== 1 failed, 1 passed in 0.35 seconds ======================

Process finished with exit code 0

説明する

このメソッドを使用してテスト ケースをマークする場合は、私が作成した形式に厳密に従う必要があります。構文は pytest.param(value, id='somthing') です。

要約する

これは、Pytest でのデータ駆動型実装の場合に当てはまります。

マスター

1. デコレーターとテスト ケースは、単一の変数を使用して複数のデータ セットを受け取り、複数の変数を使用して複数のデータ アクセス メソッドを受け取ります。

2. 異なるテストデータ形式 (リストネストされたタプル、リスト、辞書など) の場合にデータを渡し、データにアクセスする方法

3. テスト クラスを装飾するデコレータとテスト関数の違い: テスト クラスを装飾する場合、クラス内のすべてのメソッドがテスト データを送受信する必要があり、そうでない場合はエラーが報告されます。テスト関数を装飾する場合は、より柔軟です。 . 関数がデータを使用しない場合、装飾する必要はありません。

4. 出力結果を読みやすくするために、ids パラメーターの使用を選択し、テスト データ内で id パラメーター値を定義してテスト ケースを識別できます。

知らせ

1. デコレータの最初のパラメータは文字列パラメータのリストです。「a、b、c」は「a」、「b」、「c」と書くことはできません。

2. ids は文字列のリストであり、その長さはテスト データ リストの長さと一致する必要があります。

私の記事を注意深く読んでくださった皆さん、ありがとうございました!

私は過去数年間のソフトウェア テストのキャリアでまとめたいくつかの技術資料を個人的に整理しました。これには、電子書籍、履歴書モジュール、さまざまなジョブ テンプレート、インタビュー ブック、独習プロジェクトなどが含まれます。皆様、下の名刺をクリックして無料で入手してください。お見逃しなく。

   Python 自動テスト学習交換グループ:自動テストの面接履歴書学習教材のフルセットを入手できます。リンクをクリックしてグループ チャットに参加してください [Python 自動テスト交換]: http://qm.qq.com/cgi-bin/qm/ qr?_wv=1027&k=DhOSZDNS -qzT5QKbFQMsfJ7DsrFfKpOF&authKey=eBt%2BF%2FBK81lVLcsLKaFqnvDAVA8IdNsGC7J0YV73w8V%2FJpdbby66r7vJ1rsPIifg&noverify=0&group_code=1984 08628

おすすめ

転載: blog.csdn.net/MXB_1220/article/details/131755708