Tutorial de PyTest: qué es, cómo instalarlo, marco, afirmaciones
¿Qué es PyTest?
Prueba de Py es un marco de prueba que permite a los usuarios escribir códigos de prueba usando Python Lenguaje de programación. Te ayuda a escribir casos de prueba simples y escalables para bases de datos, API o UI. PyTest se utiliza principalmente para escribir pruebas para API. Ayuda a escribir pruebas desde pruebas unitarias simples hasta pruebas funcionales complejas.
¿Por qué utilizar PyTest?
Algunas de las ventajas de pytest son
- Es muy fácil empezar debido a su sintaxis sencilla y sencilla.
- Puede ejecutar pruebas en paralelo.
- Puede ejecutar una prueba específica o un subconjunto de pruebas.
- Detectar pruebas automáticamente
- Saltar pruebas
- Fuente abierta
Cómo instalar PyTest
A continuación se muestra un proceso sobre cómo instalar PyTest:
Paso 1) Puedes instalar pytest mediante
pip install pytest==2.9.1
Una vez completada la instalación, puede confirmarla con
py.test -h
Esto mostrará la ayuda.
Primera prueba básica de Py
Ahora aprenderemos cómo usar Pytest con un ejemplo básico de PyTest.
Crea una carpeta Study_pytest. Vamos a crear nuestros archivos de prueba dentro de esta carpeta.
Navegue hasta esa carpeta en su línea de comando.
Cree un archivo llamado test_sample1.py dentro de la carpeta
Agregue el siguiente código y guárdelo.
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"
Ejecute la prueba usando el comando
py.test
Obtendrás resultados 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
Aquí en test_sample1.py F.
F dice fracaso
El punto (.) dice éxito.
En la sección de fallas, puede ver los métodos fallidos y la línea de falla. Aquí x==y significa 5==6 lo cual es falso.
A continuación, en este tutorial de PyTest, aprenderemos sobre la aserción en PyTest.
Afirmaciones en PyTest
Las aserciones de Pytest son comprobaciones que devuelven el estado Verdadero o Falso. En Python Pytest, si una aserción falla en un método de prueba, la ejecución de ese método se detiene allí. El código restante en ese método de prueba no se ejecuta y las afirmaciones de Pytest continuarán con el siguiente método de prueba.
Ejemplos de afirmación de 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 en test_file1_method1() en lugar de la afirmación
assert x == y,"test failed"
Ejecutar la prueba dará como resultado el error. AssertionError: prueba fallida x=5 y=6
Cómo identifica PyTest los archivos de prueba y los métodos de prueba
Por defecto, pytest solo identifica los nombres de archivos que comienzan con prueba_ o terminando con _prueba como los archivos de prueba. Sin embargo, podemos mencionar explícitamente otros nombres de archivo (se explica más adelante). Pytest requiere que los nombres de los métodos de prueba comiencen con "prueba.” Todos los demás nombres de métodos se ignorarán incluso si solicitamos explícitamente ejecutar esos métodos.
Vea algunos ejemplos de nombres de archivos pytest válidos e inválidos
test_login.py - valid login_test.py - valid testlogin.py -invalid logintest.py -invalid
Nota: Sí, podemos pedirle explícitamente a pytest que elija testlogin.py y logintest.py
Vea algunos ejemplos de métodos de prueba de pytest válidos e inválidos.
def test_file1_method1(): - valid def testfile1_method1(): - valid def file1_method1(): - invalid
Nota: Incluso si mencionamos explícitamente file1_method1() pytest no ejecutará este método.
Ejecute varias pruebas desde un archivo específico y varios archivos
Actualmente, dentro de la carpeta Study_pytest, tenemos un archivo test_sample1.py. Supongamos que tenemos varios archivos, digamos test_sample2.py, test_sample3.py. Para ejecutar todas las pruebas de todos los archivos en la carpeta y subcarpetas, simplemente necesitamos ejecutar el comando pytest.
py.test
Esto ejecutará todos los nombres de archivos que comiencen con test_ y los nombres de archivos que terminen con _test en esa carpeta y las subcarpetas debajo de esa carpeta.
Para ejecutar pruebas solo desde un archivo específico, podemos usar py.test
py.test test_sample1.py
Ejecute un subconjunto de prueba completa con PyTest
A veces no queremos ejecutar todo el conjunto de pruebas. Pytest nos permite ejecutar pruebas específicas. Podemos hacerlo de 2 maneras
- Agrupación de nombres de pruebas por coincidencia de subcadenas
- Agrupación de pruebas por marcadores.
Ya tenemos test_sample1.py. Cree un archivo test_sample2.py y agregue el siguiente código
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"
Entonces tenemos actualmente
• test_sample1.py • test_file1_method1() • test_file1_method2() • test_sample2.py • test_file2_method1() • test_file2_method2()
Opción 1) Ejecutar pruebas mediante coincidencia de subcadenas
Aquí, para ejecutar todas las pruebas que tienen el método 1 en su nombre, tenemos que ejecutar
py.test -k method1 -v -k <expression> is used to represent the substring to match -v increases the verbosity
Entonces, ejecutar py.test -k method1 -v le dará el siguiente 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 ===============================
Aquí puedes ver hacia el final. 2 pruebas deseleccionadas por '-kmethod1' que son test_file1_method2 y test_file2_method2
Intente ejecutar con varias combinaciones 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'
Opción 2) Ejecutar pruebas por marcadores
Pytest nos permite establecer varios atributos para los métodos de prueba usando marcadores de pytest, @pytest.mark. Para usar marcadores en el archivo de prueba, necesitamos importar pytest en los archivos de prueba.
Aquí aplicaremos diferentes nombres de marcadores a los métodos de prueba y ejecutaremos pruebas específicas basadas en los nombres de los marcadores. Podemos definir los marcadores en los nombres de cada prueba usando
@pytest.mark.<name>.
Estamos definiendo los marcadores set1 y set2 en los métodos de prueba y ejecutaremos la prueba utilizando los nombres de los marcadores. Actualice los archivos de prueba con el siguiente código
prueba_muestra1.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"
prueba_muestra2.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 ejecutar la prueba marcada por
py.test -m <name> -m <name> mentions the marker name
Ejecute py.test -m set1. Esto ejecutará los métodos test_file1_method1, test_file2_method1, test_file2_method2.
Al ejecutar py.test -m set2 se ejecutará test_file1_method2.
Ejecute pruebas en paralelo con Pytest
Por lo general, un conjunto de pruebas tendrá varios archivos de prueba y cientos de métodos de prueba cuya ejecución llevará una cantidad considerable de tiempo. Pytest nos permite ejecutar pruebas en paralelo.
Para eso primero necesitamos instalar pytest-xdist ejecutando
pip install pytest-xdist
Puede ejecutar pruebas ahora por
py.test -n 4
-n ejecuta las pruebas utilizando varios trabajadores. En el comando anterior, habrá 4 trabajadores para ejecutar la prueba.
Calendario de Pytest
Los accesorios se utilizan cuando queremos ejecutar algún código antes de cada método de prueba. Entonces, en lugar de repetir el mismo código en cada prueba, definimos accesorios. Por lo general, los dispositivos se utilizan para inicializar conexiones de bases de datos, pasar la base, etc.
Un método se marca como dispositivo Pytest marcándolo con
@pytest.fixture
Un método de prueba puede utilizar un dispositivo Pytest mencionando el dispositivo como parámetro de entrada.
Cree un nuevo archivo test_basic_fixture.py con el siguiente 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"
Aquí
- Tenemos un dispositivo llamado Supply_AA_BB_CC. Este método devolverá una lista de 3 valores.
- Disponemos de 3 métodos de prueba comparando cada uno de los valores.
Cada una de las funciones de prueba tiene un argumento de entrada cuyo nombre coincide con un dispositivo disponible. Luego, Pytest invoca el método de fijación correspondiente y los valores devueltos se almacenarán en el argumento de entrada, aquí la lista [25,35,45]. Ahora los elementos de la lista se utilizan en métodos de prueba para la comparación.
Ahora ejecuta la prueba y mira el 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 =================================
La prueba test_comparewithBB se pasa desde zz=BB=35 y las 2 pruebas restantes fallan.
El método de fijación tiene un alcance solo dentro del archivo de prueba en el que está definido. Si intentamos acceder al dispositivo en algún otro archivo de prueba, obtendremos un error que dice dispositivo 'suministro_AA_BB_CC' no encontrado para los métodos de prueba en otros archivos.
Para usar el mismo dispositivo en múltiples archivos de prueba, crearemos métodos de dispositivo en un archivo llamado conftest.py.
Veamos esto con el siguiente ejemplo de PyTest. Cree tres archivos conftest.py, test_basic_fixture.py, test_basic_fixture3.py con el siguiente código
concurso.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 buscará primero el dispositivo en el archivo de prueba y, si no lo encuentra, buscará en conftest.py
Ejecute la prueba mediante py.test -k test_comparewith -v para obtener el resultado como se muestra a continuación
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
Prueba parametrizada de Pytest
El propósito de parametrizar una prueba es ejecutar una prueba con múltiples conjuntos de argumentos. Podemos hacer esto con @pytest.mark.parametrize.
Veremos esto con el siguiente ejemplo de PyTest. Aquí pasaremos 3 argumentos a un método de prueba. Este método de prueba agregará los primeros 2 argumentos y los comparará con el tercer argumento.
Cree el archivo de prueba test_addition.py con el siguiente código
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"
Aquí el método de prueba acepta tres argumentos: entrada3, entrada1, salida. Suma entrada2 y entrada1 y los compara con la salida.
Ejecutemos la prueba con py.test -k test_add -v y veamos el 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
Puede ver que las pruebas se ejecutaron 2 veces: una comprobando 5+5 ==10 y otra comprobando 3+5 ==12
test_addition.py::test_add[5-5-10] PASADO
test_addition.py::test_add[3-5-12] FALLÓ
Pytest Xfail/Omitir pruebas
Habrá algunas situaciones en las que no queremos ejecutar una prueba o una caso de prueba no es relevante para un momento en particular. En esas situaciones, tenemos la opción de Xfallar la prueba u omitir las pruebas.
La prueba fallida se ejecutará, pero no se contará como parte de las pruebas fallidas o aprobadas. No se mostrará ningún seguimiento si esa prueba falla. Podemos ejecutar pruebas fallidas usando
@pytest.mark.xfail.
Omitir una prueba significa que la prueba no se ejecutará. Podemos omitir pruebas usando
@pytest.mark.skip.
Edite test_addition.py con el siguiente código
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"
Aquí
- test_add_1 y test_add_2 se omiten y no se ejecutarán.
- test_add_3 y test_add_4 están fallidos. Estas pruebas se ejecutarán y formarán parte de las pruebas xfailed (al fallar la prueba) o xpassed (al aprobar la prueba). No habrá ningún seguimiento de las fallas.
- test_add_5 y test_add_6 se ejecutarán y test_add_6 informará una falla con el rastreo mientras pasa test_add_5
Ejecute la prueba mediante py.test test_addition.py -v y vea el 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 =================
Resultados XML
Podemos crear resultados de pruebas en formato XML que podemos enviar a servidores de integración continua para su posterior procesamiento, etc. Esto se puede hacer por
py.test test_sample1.py -v –junitxml=”resultado.xml”
result.xml registrará el resultado de la ejecución de la prueba. Encuentre un resultado de muestra.xml a continuación
<?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 un total de dos pruebas, de las cuales una falló. A continuación, puede ver los detalles de cada prueba ejecutada en etiqueta.
Marco Pytest probando una API
Ahora crearemos un pequeño marco pytest para probar una API. La API aquí utilizada es gratuita de https://reqres.in/. Este sitio web es solo para proporcionar una API comprobable. Este sitio web no almacena nuestros datos.
Aquí escribiremos algunas pruebas para
- enumerando algunos usuarios
- iniciar sesión con usuarios
Cree los siguientes archivos con el código proporcionado
conftest.py: tiene un dispositivo que proporcionará la URL base para todos los métodos de prueba
import pytest @pytest.fixture def supply_url(): return "https://reqres.in/api"
test_list_user.py: contiene los métodos de prueba para enumerar usuarios válidos e inválidos
- test_list_valid_user prueba la búsqueda de usuarios válidos y verifica la respuesta
- test_list_invaliduser prueba la recuperación de usuarios no válidos y verifica la respuesta
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: contiene métodos de prueba para probar la funcionalidad de inicio de sesión.
- test_login_valid prueba el intento de inicio de sesión válido con correo electrónico y contraseña
- test_login_no_password prueba el intento de inicio de sesión no válido sin pasar la contraseña
- test_login_no_email prueba el intento de inicio de sesión no válido sin pasar el correo electrónico.
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
Ejecute la prueba usando py.test -v
Ver el 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
Actualice las pruebas y pruebe varias salidas.
Resumen
En este tutorial de PyTest, cubrimos
- Instalar pytest usando instalación de pip pytest=2.9.1
- Programa pytest simple y ejecútelo con el comando py.test.
- Las declaraciones de afirmación, afirmar x==y, devolverán Verdadero o Falso.
- Cómo pytest identifica archivos y métodos de prueba.
- Archivos de prueba que comienzan con prueba_ o terminando con _prueba
- Métodos de prueba que comienzan con testea
- El comando py.test ejecutará todos los archivos de prueba en esa carpeta y subcarpetas. Para ejecutar un archivo específico, podemos usar el comando py.test
- Ejecutar un subconjunto de métodos de prueba
- La agrupación de nombres de pruebas por subcadena match.py.test -k -v ejecutará todas las pruebas que tengan en su nombre.
- Ejecute la prueba mediante marcadores. Marque las pruebas usando @pytest.mark. y ejecute las pruebas usando pytest -m para ejecutar las pruebas marcadas como .
- Ejecutar pruebas en paralelo
- Instale pytest-xdist usando pip install pytest-xdist
- Ejecute pruebas usando py.test -n NUM donde NUM es la cantidad de trabajadores
- Crear métodos de fijación para ejecutar código antes de cada prueba marcando el método con @pytest.fixture
- El alcance de un método de fijación está dentro del archivo en el que está definido.
- Se puede acceder a un método de fijación a través de varios archivos de prueba definiéndolo en el archivo conftest.py.
- Un método de prueba puede acceder a un dispositivo Pytest usándolo como argumento de entrada.
- Parametrización de pruebas para ejecutarlas en múltiples conjuntos de entradas.
@pytest.mark.parametrize(“entrada1, entrada2, salida”,[(5,5,10),(3,5,12)])
def test_add(entrada1, entrada2, salida):
afirmar entrada1+entrada2 == salida, "fallido"
ejecutará la prueba con las entradas (5,5,10) y (3,5,12) - Omitir/xfail pruebas usando @pytets.mark.skip y @pytest.mark.xfail
- Cree resultados de pruebas en formato XML que cubran los detalles de las pruebas ejecutadas utilizando py.test test_sample1.py -v –junitxml=”result.xml”
- Un marco de prueba de muestra para probar una API