Parametrização do Pytest - as habilidades que você não conhece

Índice

prefácio

Decore a aula de teste

saída

ilustrar

Decore a função de teste

dados únicos

saída

ilustrar

um conjunto de dados

saída

ilustrar

Correspondência Gráfica

dados combinados

saída

ilustrar

marcar caso de uso

saída

ilustrar

dicionários aninhados

saída

aumentar a legibilidade

Use o parâmetro ids

saída

ilustrar

ID personalizado para identificação

saída

ilustrar

Resumir

Resumir:


prefácio

A estrutura de teste de unidade de teste unitário usa DDT para testes orientados a dados, então como pode a estrutura Pytest mais poderosa e flexível não ter o conceito de orientado a dados? Na verdade, o Pytest usa o decorador @pytest.mark.parametrize para implementar testes orientados a dados, então hoje falaremos simplesmente sobre como ele realiza testes orientados a dados

Decore a aula de teste

"""
------------------------------------
@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'])

saída

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

ilustrar

Quando o decorador decora a classe de teste, a coleta de dados será passada para todos os métodos da classe

Decore a função de teste

dados únicos

"""
------------------------------------
@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'])

saída

============================= 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

ilustrar

Quando o caso de teste precisa apenas de um parâmetro, armazenamos a sequência aninhada não ordenada de listas de dados, e o primeiro parâmetro do decorador @pytest.mark.parametrize('a', data) também precisa apenas de uma variável para receber cada parâmetro em os elementos da lista, o segundo parâmetro passa uma lista de dados armazenados, então o caso de teste precisa usar uma string com o mesmo nome para receber os dados de teste (a no exemplo) e tantos casos de teste quantos elementos houver no lista será gerada e executada

um conjunto de dados

"""
------------------------------------
@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'])

saída

============================= 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

ilustrar

Quando um caso de teste requer vários dados, podemos usar uma lista de sequências aninhadas (tuplas aninhadas e listas aninhadas) para armazenar dados de teste

O decorador @pytest.mark.parametrize() pode receber dados usando uma única variável ou várias variáveis. Da mesma forma, a função do caso de teste precisa ser consistente com ela

Ao usar uma única variável para receber, quando os dados de teste são passados ​​para a função de teste, é cada elemento da lista ou uma pequena lista, e cada dado precisa ser obtido por indexação

Ao usar várias variáveis ​​para receber dados, cada variável recebe cada elemento em uma pequena lista ou tupla separadamente

Quantos conjuntos de pequenas listas ou tuplas estão aninhados na lista e quantos casos de teste são gerados

Correspondência Gráfica

dados combinados

"""
------------------------------------
@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'])

saída

============================= 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

ilustrar

Através dos resultados do teste, não é difícil para nós analisar que uma função de teste também pode ser decorada por vários decoradores parametrizados ao mesmo tempo, então os dados em vários decoradores serão passados ​​para a função de teste de maneira cruzada, e então n*n testes serão gerados Casos de uso, que também fornecem conveniência para nosso design de teste

marcar caso de uso

Os casos de teste podem ser marcados diretamente e os decoradores parametrizados também podem ser reconhecidos (marcando os casos de teste como com falha ou ignorados)

Marcado como ignorado incondicionalmente (marcado como xfail por falha, tente você mesmo)

"""
------------------------------------
@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'])

saída

============================= 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

ilustrar

A saída mostra que 2 casos de uso foram coletados, um aprovado e o outro ignorado. Quando não queremos executar um determinado conjunto de dados de teste, podemos marcar skip ou skipif; quando esperamos que um determinado conjunto de dados falhe, podemos marcá-lo como xfail wait

dicionários aninhados

"""
------------------------------------
@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'])

saída

============================= 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

aumentar a legibilidade

Use o parâmetro ids

O decorador parametrizado possui IDs de parâmetro adicionais, que podem identificar cada caso de teste e personalizar a exibição dos resultados dos dados do teste. Para aumentar a legibilidade, podemos marcar quais dados de teste são usados ​​por cada caso de teste e adicionar alguns ilustrados adequadamente

Antes de usá-lo, você precisa saber que o parâmetro ids deve ser uma lista de strings, que deve ser consistente com o comprimento da lista de objetos de dados. Podemos tentar usar ids para ver o efeito

"""
------------------------------------
@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 : 更加详细的输出测试结果

saída

O decorador não passa a saída do parâmetro 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

O decorador passa a saída do parâmetro 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

ilustrar

Usei -v para executar o comando, e os resultados de saída serão exibidos com mais detalhes. Você pode ver que todos os casos de uso nos resultados de dados estão claramente marcados por uma lista e, por meio dessa marca, pode ser visto de forma mais intuitiva Cada teste O nome dos dados e o conteúdo do teste usado no caso de uso

ID personalizado para identificação

Além de usar o parâmetro ids para aumentar a legibilidade da saída, também podemos definir um valor id ao lado do parâmetro na lista de parâmetros para identificação, veja o exemplo a seguir

"""
------------------------------------
@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'])

saída

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

ilustrar

Se você usar este método para marcar casos de teste, você deve seguir estritamente o formato que escrevi. A sintaxe é pytest.param(value, id='somthing')

Resumir

Este é o caso da implementação baseada em dados no Pytest

mestre

1. Decoradores e casos de teste usam uma única variável para receber vários conjuntos de dados e várias variáveis ​​para receber vários métodos de acesso a dados

2. Como passar dados e acessar dados quando diferentes formulários de dados de teste (listar tuplas aninhadas, listas, dicionários, etc.)

3. A diferença entre o decorador decorando a classe de teste e a função de teste: Ao decorar a classe de teste, todos os métodos da classe devem receber e enviar dados de teste, caso contrário, um erro será relatado. É mais flexível ao decorar a função de teste . Se a função não usa dados, ela não precisa ser decorada

4. Para facilitar a leitura dos resultados de saída, você pode optar por usar o parâmetro ids e definir o valor do parâmetro id nos dados de teste para identificar o caso de teste

Perceber

1. O primeiro parâmetro do decorador é uma lista de parâmetros de string "a, b, c" não pode ser escrito como "a", "b", "c"

2. ids é uma lista de strings e seu comprimento precisa ser consistente com o comprimento da lista de dados de teste

Obrigado a todos que leram meu artigo com atenção! ! !

Eu pessoalmente selecionei alguns materiais técnicos que compilei em minha carreira de teste de software nos últimos anos, incluindo: e-books, módulos de currículo, vários modelos de trabalho, livros de entrevistas, projetos de auto-estudo, etc. Sejam todos bem-vindos a clicar no cartão de visita abaixo para obtê-lo gratuitamente, não perca.

   Grupo de troca de aprendizado de teste automatizado Python: um conjunto completo de materiais de aprendizado de resumo de entrevista de teste automatizado para obter Clique no link para entrar no bate-papo do grupo [troca de teste automatizado de python]: http://qm.qq.com/cgi-bin/qm/ qr?_wv=1027&k=DhOSZDNS -qzT5QKbFQMsfJ7DsrFfKpOF&authKey=eBt%2BF%2FBK81lVLcsLKaFqnvDAVA8IdNsGC7J0YV73w8V%2FJpdbby66r7vJ1rsPIifg&noverify=0&group_code= 198408628

Acho que você gosta

Origin blog.csdn.net/MXB_1220/article/details/131755708
Recomendado
Clasificación