Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Industrial programming >> python

Tutorial PyTest:O que é, como instalar, estrutura, asserções

O que é PyTest?


PyTest é uma estrutura de teste que permite aos usuários escrever códigos de teste usando a linguagem de programação Python. Ele ajuda você a escrever casos de teste simples e escaláveis ​​para bancos de dados, APIs ou UI. PyTest é usado principalmente para escrever testes para APIs. Ele ajuda a escrever testes desde testes unitários simples até testes funcionais complexos.

Por que usar o PyTest?


Algumas das vantagens do pytest são

Neste tutorial do Python PyTest, você aprenderá:

Como instalar o PyTest


A seguir está um processo sobre como instalar o PyTest:

Etapa 1) Você pode instalar o pytest por
pip install pytest==2.9.1

Quando a instalação estiver concluída, você pode confirmá-la com
py.test -h

Isso exibirá a ajuda


Primeiro PyTest básico


Agora, aprenderemos como usar o Pytest com um exemplo básico do PyTest.

Crie uma pasta study_pytest. Vamos criar nossos arquivos de teste dentro desta pasta.

Por favor, navegue até essa pasta em sua linha de comando.

Crie um arquivo chamado test_sample1.py dentro da pasta



Adicione o código abaixo e salve

import pytest
def test_file1_method1():
	x=5
	y=6
	assert x+1 == y,"test failed"
	assert x == y,"test failed"
def test_file1_method2():
	x=5
	y=6
	assert x+1 == y,"test failed" 

Execute o teste usando o comando
py.test

Você obterá a saída como
test_sample1.py F.
============================================== FAILURES ========================================
____________________________________________ test_sample1 ______________________________________
    def test_file1_method1():
    	x=5
    	y=6
       	assert x+1 == y,"test failed"
>      	assert x == y,"test failed"
E       AssertionError: test failed
E       assert 5 == 6
test_sample1.py:6: AssertionError



Aqui em test_sample1.py F.

F diz falha

Ponto(.) indica sucesso.

Na seção de falhas, você pode ver o(s) método(s) com falha e a linha de falha. Aqui x==y significa 5==6 que é falso.

A seguir, neste tutorial do PyTest, aprenderemos sobre asserção no PyTest.

Declarações no PyTest


As asserções do Pytest são verificações que retornam o status True ou False. No Python Pytest, se uma asserção falhar em um método de teste, a execução desse método será interrompida lá. O código restante nesse método de teste não é executado e as asserções do Pytest continuarão com o próximo método de teste.

Exemplos de declaração do Pytest:
assert "hello" == "Hai" is an assertion failure.
assert 4==4 is a successful assertion
assert True is a successful assertion
assert False is an assertion failure.

Considerar
assert x == y,"test failed because x=" + str(x) + " y=" + str(y)

Coloque este código em test_file1_method1() em vez da asserção
assert x == y,"test failed"

Executar o teste resultará em falha como AssertionError:teste falhou x=5 y=6

Como o PyTest identifica os arquivos de teste e os métodos de teste


Por padrão, o pytest identifica apenas os nomes de arquivo que começam com test_ ou terminando com _test como os arquivos de teste. Podemos mencionar explicitamente outros nomes de arquivos (explicados posteriormente). O Pytest requer que os nomes dos métodos de teste comecem com “test .” Todos os outros nomes de métodos serão ignorados mesmo se pedirmos explicitamente para executar esses métodos.

Veja alguns exemplos de nomes de arquivos pytest válidos e inválidos
test_login.py - valid
login_test.py - valid
testlogin.py -invalid
logintest.py -invalid

Nota:Sim, podemos pedir explicitamente ao pytest para escolher testlogin.py e logintest.py

Veja alguns exemplos de métodos de teste pytest válidos e inválidos
def test_file1_method1(): - valid
def testfile1_method1(): - valid
def file1_method1(): - invalid	

Nota:Mesmo se mencionarmos explicitamente file1_method1(), pytest não executará este método.

Executar vários testes de um arquivo específico e vários arquivos


Atualmente, dentro da pasta study_pytest, temos um arquivo test_sample1.py. Suponha que temos vários arquivos, digamos test_sample2.py, test_sample3.py. Para executar todos os testes de todos os arquivos na pasta e subpastas, precisamos apenas executar o comando pytest.
py.test

Isso executará todos os nomes de arquivo começando com test_ e os nomes de arquivo que terminam com _test nessa pasta e subpastas nessa pasta.

Para executar testes apenas de um arquivo específico, podemos usar py.test
py.test test_sample1.py

Execute um subconjunto de teste inteiro com PyTest


Às vezes, não queremos executar todo o conjunto de testes. Pytest nos permite executar testes específicos. Podemos fazer de 2 maneiras
  • Agrupamento de nomes de teste por correspondência de substring
  • Agrupamento de testes por marcadores

Já temos test_sample1.py. Crie um arquivo test_sample2.py e adicione o código abaixo nele
def test_file2_method1():
	x=5
	y=6
	assert x+1 == y,"test failed"
	assert x == y,"test failed because x=" + str(x) + " y=" + str(y)
def test_file2_method2():
	x=5
	y=6
	assert x+1 == y,"test failed"

Então temos atualmente
  • test_sample1.py
    • test_file1_method1()
    • test_file1_method2()
  • test_sample2.py
    • test_file2_method1()
    • test_file2_method2()

Opção 1) Executar testes por correspondência de substring

Aqui para executar todos os testes com method1 em seu nome, temos que executar
py.test -k method1 -v
-k <expression> is used to represent the substring to match
-v increases the verbosity

Portanto, executar py.test -k method1 -v fornecerá o seguinte resultado
test_sample2.py::test_file2_method1 FAILED
test_sample1.py::test_file1_method1 FAILED

============================================== FAILURES ==============================================
_________________________________________ test_file2_method1 _________________________________________
    def test_file2_method1():
    	x=5
    	y=6
       	assert x+1 == y,"test failed"
>      	assert x == y,"test failed because x=" + str(x) + " y=" + str(y)
E       AssertionError: test failed because x=5 y=6
E       assert 5 == 6
test_sample2.py:5: AssertionError

_________________________________________ test_file1_method1 _________________________________________
    @pytest.mark.only
    def test_file1_method1():
    	x=5
    	y=6
       	assert x+1 == y,"test failed"
>      	assert x == y,"test failed because x=" + str(x) + " y=" + str(y)
E       AssertionError: test failed because x=5 y=6
E       assert 5 == 6
test_sample1.py:8: AssertionError

================================= 2 tests deselected by '-kmethod1' ==================================
=============================== 2 failed, 2 deselected in 0.02 seconds ===============================

Aqui você pode ver no final 2 testes desmarcados por '-kmethod1' que são test_file1_method2 e test_file2_method2

Tente correr com várias combinações como:-
py.test -k method -v - will run all the four methods
py.test -k methods -v – will not run any test as there is no test name matches the substring 'methods'

Opção 2) Executar testes por marcadores

O Pytest nos permite definir vários atributos para os métodos de teste usando marcadores pytest, @pytest.mark . Para usar marcadores no arquivo de teste, precisamos importar pytest nos arquivos de teste.

Aqui, aplicaremos diferentes nomes de marcadores aos métodos de teste e executaremos testes específicos com base nos nomes dos marcadores. Podemos definir os marcadores em cada nome de teste usando
@pytest.mark.<name>.			

Estamos definindo os marcadores set1 e set2 nos métodos de teste e executaremos o teste usando os nomes dos marcadores. Atualize os arquivos de teste com o seguinte código

test_sample1.py
import pytest
@pytest.mark.set1
def test_file1_method1():
	x=5
	y=6
	assert x+1 == y,"test failed"
	assert x == y,"test failed because x=" + str(x) + " y=" + str(y)

@pytest.mark.set2
def test_file1_method2():
	x=5
	y=6
	assert x+1 == y,"test failed"

test_sample2.py
import pytest
@pytest.mark.set1
def test_file2_method1():
	x=5
	y=6
	assert x+1 == y,"test failed"
	assert x == y,"test failed because x=" + str(x) + " y=" + str(y)

@pytest.mark.set1
def test_file2_method2():
	x=5
	y=6
	assert x+1 == y,"test failed"

Podemos executar o teste marcado por
py.test -m <name>
-m <name> mentions the marker name

Execute py.test -m set1. Isso executará os métodos test_file1_method1, test_file2_method1, test_file2_method2.

A execução de py.test -m set2 executará test_file1_method2.

Executar testes em paralelo com o Pytest


Normalmente, um conjunto de testes terá vários arquivos de teste e centenas de métodos de teste que levarão um tempo considerável para serem executados. Pytest nos permite executar testes em paralelo.

Para isso, precisamos primeiro instalar o pytest-xdist executando
pip install pytest-xdist



Você pode executar testes agora por

py.test -n 4

-n executa os testes usando vários trabalhadores. No comando acima, haverá 4 trabalhadores para executar o teste.

Pytest Fixtures


Fixtures são usadas quando queremos executar algum código antes de cada método de teste. Então, em vez de repetir o mesmo código em todos os testes, definimos fixtures. Normalmente, os fixtures são usados ​​para inicializar conexões de banco de dados, passar a base, etc.

Um método é marcado como um dispositivo Pytest marcando com
@pytest.fixture

Um método de teste pode usar um fixture Pytest mencionando o fixture como um parâmetro de entrada.

Crie um novo arquivo test_basic_fixture.py com o seguinte código
import pytest
@pytest.fixture
def supply_AA_BB_CC():
	aa=25
	bb =35
	cc=45
	return [aa,bb,cc]

def test_comparewithAA(supply_AA_BB_CC):
	zz=35
	assert supply_AA_BB_CC[0]==zz,"aa and zz comparison failed"

def test_comparewithBB(supply_AA_BB_CC):
	zz=35
	assert supply_AA_BB_CC[1]==zz,"bb and zz comparison failed"

def test_comparewithCC(supply_AA_BB_CC):
	zz=35
	assert supply_AA_BB_CC[2]==zz,"cc and zz comparison failed"

Aqui
  • Temos um equipamento chamado supply_AA_BB_CC. Este método retornará uma lista de 3 valores.
  • Temos 3 métodos de teste comparando com cada um dos valores.

Cada uma das funções de teste tem um argumento de entrada cujo nome corresponde a um equipamento disponível. O Pytest então invoca o método de fixture correspondente e os valores retornados serão armazenados no argumento de entrada , aqui a lista [25,35,45]. Agora os itens da lista estão sendo usados ​​em métodos de teste para comparação.

Agora execute o teste e veja o resultado
 py.test test_basic_fixture
test_basic_fixture.py::test_comparewithAA FAILED                                                                                                                                                                                       
test_basic_fixture.py::test_comparewithBB PASSED                                                                                                                                                                                       
test_basic_fixture.py::test_comparewithCC FAILED
                                                                                                                                                                                       
============================================== FAILURES ==============================================
_________________________________________ test_comparewithAA _________________________________________
supply_AA_BB_CC = [25, 35, 45]
    def test_comparewithAA(supply_AA_BB_CC):
    	zz=35
>   	assert supply_AA_BB_CC[0]==zz,"aa and zz comparison failed"
E    AssertionError: aa and zz comparison failed
E    assert 25 == 35
test_basic_fixture.py:10: AssertionError

_________________________________________ test_comparewithCC _________________________________________
supply_AA_BB_CC = [25, 35, 45]
    def test_comparewithCC(supply_AA_BB_CC):
    	zz=35
>   	assert supply_AA_BB_CC[2]==zz,"cc and zz comparison failed"
E    AssertionError: cc and zz comparison failed
E    assert 45 == 35
test_basic_fixture.py:16: AssertionError
================================= 2 failed, 1 passed in 0.05 seconds =================================

O teste test_comparewithBB foi aprovado desde zz=BB=35 e os 2 testes restantes falharam.

O método fixture tem um escopo apenas dentro do arquivo de teste em que está definido. Se tentarmos acessar o fixture em algum outro arquivo de teste, receberemos um erro dizendo que o fixture ‘supply_AA_BB_CC’ não foi encontrado para os métodos de teste em outros arquivos.

Para usar o mesmo fixture contra vários arquivos de teste, vamos criar métodos de fixture em um arquivo chamado conftest.py.

Vamos ver isso no exemplo PyTest abaixo. Crie 3 arquivos conftest.py, test_basic_fixture.py, test_basic_fixture2.py com o seguinte código

conftest.py
import pytest
@pytest.fixture
def supply_AA_BB_CC():
	aa=25
	bb =35
	cc=45
	return [aa,bb,cc]

test_basic_fixture.py
import pytest
def test_comparewithAA(supply_AA_BB_CC):
	zz=35
	assert supply_AA_BB_CC[0]==zz,"aa and zz comparison failed"

def test_comparewithBB(supply_AA_BB_CC):
	zz=35
	assert supply_AA_BB_CC[1]==zz,"bb and zz comparison failed"

def test_comparewithCC(supply_AA_BB_CC):
	zz=35
	assert supply_AA_BB_CC[2]==zz,"cc and zz comparison failed"

test_basic_fixture2.py
import pytest
def test_comparewithAA_file2(supply_AA_BB_CC):
	zz=25
	assert supply_AA_BB_CC[0]==zz,"aa and zz comparison failed"

def test_comparewithBB_file2(supply_AA_BB_CC):
	zz=25
	assert supply_AA_BB_CC[1]==zz,"bb and zz comparison failed"

def test_comparewithCC_file2(supply_AA_BB_CC):
	zz=25
	assert supply_AA_BB_CC[2]==zz,"cc and zz comparison failed"

pytest irá procurar o fixture no arquivo de teste primeiro e, se não for encontrado, ele procurará no conftest.py

Execute o teste por py.test -k test_comparewith -v para obter o resultado abaixo
test_basic_fixture.py::test_comparewithAA FAILED  
test_basic_fixture.py::test_comparewithBB PASSED 
test_basic_fixture.py::test_comparewithCC FAILED 
test_basic_fixture2.py::test_comparewithAA_file2 PASSED 
test_basic_fixture2.py::test_comparewithBB_file2 FAILED 
test_basic_fixture2.py::test_comparewithCC_file2 FAILED

Teste parametrizado do Pytest


A finalidade de parametrizar um teste é executar um teste em vários conjuntos de argumentos. Podemos fazer isso por @pytest.mark.parametrize.

Veremos isso com o exemplo PyTest abaixo. Aqui vamos passar 3 argumentos para um método de teste. Este método de teste adicionará os 2 primeiros argumentos e o comparará com o 3º argumento.

Crie o arquivo de teste test_addition.py com o código abaixo
import pytest
@pytest.mark.parametrize("input1, input2, output",[(5,5,10),(3,5,12)])
def test_add(input1, input2, output):
	assert input1+input2 == output,"failed"

Aqui o método de teste aceita 3 argumentos - entrada1, entrada2, saída. Ele adiciona input1 e input2 e compara com a saída.

Vamos executar o teste por py.test -k test_add -v e ver o resultado
test_addition.py::test_add[5-5-10] PASSED                                                                                                                                                                                              
test_addition.py::test_add[3-5-12] FAILED                                                                                                                                                                                              
============================================== FAILURES ==============================================
__________________________________________ test_add[3-5-12] __________________________________________
input1 = 3, input2 = 5, output = 12
    @pytest.mark.parametrize("input1, input2, output",[(5,5,10),(3,5,12)])
    def test_add(input1, input2, output):
>   	assert input1+input2 == output,"failed"
E    AssertionError: failed
E    assert (3 + 5) == 12
test_addition.py:5: AssertionError

Você pode ver os testes executados 2 vezes – um verificando 5+5 ==10 e outro verificando 3+5 ==12

test_addition.py::test_add[5-5-10] APROVADO

test_addition.py::test_add[3-5-12] FALHA

Pytest Xfail / Saltar testes


Haverá algumas situações em que não queremos executar um teste ou um caso de teste não é relevante para um determinado momento. Nessas situações, temos a opção de Xfail no teste ou pular os testes

O teste xfailed será executado, mas não será contado como parte dos testes reprovados ou aprovados. Não haverá traceback exibido se esse teste falhar. Podemos xfail testes usando

@pytest.mark.xfail.

Ignorar um teste significa que o teste não será executado. Podemos pular testes usando

@pytest.mark.skip.

Edite o test_addition.py com o código abaixo
import pytest
@pytest.mark.skip
def test_add_1():
	assert 100+200 == 400,"failed"

@pytest.mark.skip
def test_add_2():
	assert 100+200 == 300,"failed"

@pytest.mark.xfail
def test_add_3():
	assert 15+13 == 28,"failed"

@pytest.mark.xfail
def test_add_4():
	assert 15+13 == 100,"failed"

def test_add_5():
	assert 3+2 == 5,"failed"

def test_add_6():
	assert 3+2 == 6,"failed"

Aqui
  • test_add_1 e test_add_2 são ignorados e não serão executados.
  • test_add_3 e test_add_4 falharam com x. Esses testes serão executados e farão parte dos testes xfailed(na falha do teste) ou xpassed(na aprovação do teste). Não haverá rastreamento de falhas.
  • test_add_5 e test_add_6 serão executados e test_add_6 reportará falha com traceback enquanto test_add_5 passar

Execute o teste por py.test test_addition.py -v e veja o resultado
test_addition.py::test_add_1 SKIPPED
test_addition.py::test_add_2 SKIPPED
test_addition.py::test_add_3 XPASS
test_addition.py::test_add_4 xfail
test_addition.py::test_add_5 PASSED
test_addition.py::test_add_6 FAILED

============================================== FAILURES ==============================================
_____________________________________________ test_add_6 _____________________________________________
    def test_add_6():
>   	assert 3+2 == 6,"failed"
E    AssertionError: failed
E    assert (3 + 2) == 6
test_addition.py:24: AssertionError

================ 1 failed, 1 passed, 2 skipped, 1 xfailed, 1 xpassed in 0.07 seconds =================

XML de resultados


Podemos criar resultados de teste em formato XML que podemos alimentar em servidores de Integração Contínua para processamento adicional e assim por diante. Isso pode ser feito por

py.test test_sample1.py -v –junitxml=”result.xml”

O result.xml registrará o resultado da execução do teste. Encontre um exemplo de result.xml abaixo
<?xml version="1.0" encoding="UTF-8"?>
<testsuite errors="0" failures="1" name="pytest" skips="0" tests="2" time="0.046">
   <testcase classname="test_sample1" file="test_sample1.py" line="3" name="test_file1_method1" time="0.001384973526">
     <failure message="AssertionError:test failed because x=5 y=6 assert 5 ==6">
    @pytest.mark.set1
    def test_file1_method1():
    	x=5
    	y=6
       	assert x+1 == y,"test failed"
>      	assert x == y,"test failed because x=" + str(x) + " y=" + str(y)
E       AssertionError: test failed because x=5 y=6
E       assert 5 == 6
         test_sample1.py:9: AssertionError
    </failure>
   </testcase>
   <testcase classname="test_sample1" file="test_sample1.py" line="10" name="test_file1_method2" time="0.000830173492432" />
</testsuite>

De podemos ver um total de dois testes dos quais um falhou. Abaixo você pode ver os detalhes de cada teste executado na tag .

Pytest Framework testando uma API


Agora vamos criar um pequeno framework pytest para testar uma API. A API aqui usada é gratuita de https://reqres.in/. Este site é apenas para fornecer API testável. Este site não armazena nossos dados.

Aqui vamos escrever alguns testes para
  • listando alguns usuários
  • faça login com usuários

Crie os arquivos abaixo com o código fornecido

conftest.py – tem um fixture que fornecerá url base para todos os métodos de teste
import pytest
@pytest.fixture
def supply_url():
	return "https://reqres.in/api"

test_list_user.py – contém os métodos de teste para listar usuários válidos e inválidos
  • test_list_valid_user testa a busca válida do usuário e verifica a resposta
  • test_list_invaliduser testa a busca de usuário inválido e verifica a resposta
import pytest
import requests
import json
@pytest.mark.parametrize("userid, firstname",[(1,"George"),(2,"Janet")])
def test_list_valid_user(supply_url,userid,firstname):
	url = supply_url + "/users/" + str(userid)
	resp = requests.get(url)
	j = json.loads(resp.text)
	assert resp.status_code == 200, resp.text
	assert j['data']['id'] == userid, resp.text
	assert j['data']['first_name'] == firstname, resp.text

def test_list_invaliduser(supply_url):
	url = supply_url + "/users/50"
	resp = requests.get(url)
	assert resp.status_code == 404, resp.text

test_login_user.py – contém métodos de teste para testar a funcionalidade de login.
  • test_login_valid testa a tentativa de login válida com e-mail e senha
  • test_login_no_password testa a tentativa de login inválida sem passar a senha
  • test_login_no_email testa a tentativa de login inválida sem passar o e-mail.
import pytest
import requests
import json
def test_login_valid(supply_url):
	url = supply_url + "/login/" 
	data = {'email':'test@test.com','password':'something'}
	resp = requests.post(url, data=data)
	j = json.loads(resp.text)
	assert resp.status_code == 200, resp.text
	assert j['token'] == "QpwL5tke4Pnpja7X", resp.text

def test_login_no_password(supply_url):
	url = supply_url + "/login/" 
	data = {'email':'test@test.com'}
	resp = requests.post(url, data=data)
	j = json.loads(resp.text)
	assert resp.status_code == 400, resp.text
	assert j['error'] == "Missing password", resp.text

def test_login_no_email(supply_url):
	url = supply_url + "/login/" 
	data = {}
	resp = requests.post(url, data=data)
	j = json.loads(resp.text)
	assert resp.status_code == 400, resp.text
	assert j['error'] == "Missing email or username", resp.text

Execute o teste usando py.test -v

Veja o resultado como
test_list_user.py::test_list_valid_user[1-George] PASSED                                                                                                                                                                               
test_list_user.py::test_list_valid_user[2-Janet] PASSED                                                                                                                                                                                
test_list_user.py::test_list_invaliduser PASSED                                                                                                                                                                                        
test_login_user.py::test_login_valid PASSED                                                                                                                                                                                            
test_login_user.py::test_login_no_password PASSED                                                                                                                                                                                      
test_login_user.py::test_login_no_email PASSED

Atualize os testes e tente várias saídas

Resumo


Neste tutorial do PyTest, abordamos
  • Instale o pytest usando pip install pytest=2.9.1
  • Programa pytest simples e execute-o com o comando py.test.
  • Declarações assertivas, assert x==y, retornarão True ou False.
  • Como o pytest identifica arquivos e métodos de teste.
  • Arquivos de teste começando com test_ ou terminando com _test
  • Métodos de teste começando com teste
  • O comando
  • py.test executará todos os arquivos de teste nessa pasta e subpastas. Para executar um arquivo específico, podemos usar o comando py.test
  • Execute um subconjunto de métodos de teste
  • O agrupamento de nomes de teste por substring matching.py.test -k -v executará todos os testes com em seu nome.
  • Execute o teste por marcadores.Marque os testes usando @pytest.mark. e execute os testes usando pytest -m para executar testes marcados como .
  • Executar testes em paralelo
  • Instale pytest-xdist usando pip install pytest-xdist
  • Execute testes usando py.test -n NUM onde NUM é o número de trabalhadores
  • Criando métodos de fixture para executar código antes de cada teste marcando o método com @pytest.fixture
  • O escopo de um método de fixture está dentro do arquivo em que foi definido.
  • Um método de fixture pode ser acessado em vários arquivos de teste definindo-o no arquivo conftest.py.
  • Um método de teste pode acessar um dispositivo Pytest usando-o como um argumento de entrada.
  • Parametrizando testes para executá-lo em vários conjuntos de entradas.
    @pytest.mark.parametrize(“input1, input2, output”,[(5,5,10),(3,5,12)] )
    def test_add(input1, input2, output):
    assert input1+input2 ==output,”failed”
    vai executar o teste com as entradas (5,5,10) e (3 ,5,12)
  • Ignore/xfail testes usando @pytets.mark.skip e @pytest.mark.xfail
  • Crie resultados de teste em formato XML que abranja os detalhes do teste executado usando py.test test_sample1.py -v –junitxml=”result.xml”
  • Um exemplo de framework pytest para testar uma API

python

  1. Troque duas variáveis ​​Python sem usar uma terceira
  2. Funções do Python Lambda com EXEMPLOS
  3. Função Python round() com EXEMPLOS
  4. Python Obter hora atual
  5. Python - Data e hora
  6. Variáveis, constantes e literais do Python