Explicación detallada de la herramienta de prueba de Python sobre el uso de Pytest

1. Introducción

Pytest es una herramienta de prueba de Python con todas las funciones que admite complementos de extensión de terceros y se puede utilizar para realizar pruebas unitarias y pruebas funcionales complejas. Se puede utilizar junto con selenium, request, appium y otros módulos para implementar pruebas automatizadas de WEB UI, API y APP.
Para obtener más detalles, consulte el documento de la guía de referencia: https://docs.pytest.org/en/7.1.x/#
Documento PDF: https://media.readthedocs.org/pdf/pytest/latest/pytest.pdf

2. Instalación

0. Requisito previo: Se ha instalado y configurado el entorno Python 3.7+
1. Instalación: Ejecute el comando pip install pytest en la línea de comando o terminal Pycharm
2. Verificación: Ejecute el comando pytest --version y se mostrará el número de versión de pytest instalado. se mostrará.

3. uso

1. Reglas de Pytest para descubrir casos de prueba

  • El archivo py comienza con test_ o termina con _test;
  • El nombre de la clase comienza con Prueba;
  • Los casos de prueba comienzan con test_.

2. Operación básica de Pytest

Escriba un caso de uso de acuerdo con las reglas de casos de uso de descubrimiento de Pytest y ejecútelo directamente. Puede llamar a Pytest para recopilar casos de prueba que cumplan con las reglas, ejecutar los casos de uso e imprimir el estado de ejecución y otra información.

"""
   Pytest运行demo
"""

class TestPytest():
    """测试Pytest类"""

    def test_001(self):
        """test_001"""
        print("test_001")
        assert 1 == 1

    def test_002(self):
        """test_002"""
        print("test_002")
        assert 1 < 2

    def test_003(self):
        """test_003"""
        print("test_003")
        assert 1 != 2

    def test_004(self):
        """test_004"""
        print("test_004")
        assert 1 == 0

El cuadro rojo en la figura siguiente brinda la información del resultado de la prueba, incluidos los resultados de la ejecución y el consumo de tiempo de cada caso de uso, el número de pases y fallas, etc.; la parte azul es la información de inicio; la parte del cuadro verde presenta la ejecución. proceso, falla de aserción y otra información.

3. Funciones de uso común

3.1 Archivo de configuración pytest.ini

El archivo pytest.ini tiene prioridad sobre otros archivos. Incluso si está vacío, se pueden configurar varios parámetros de ejecución en este archivo. Consulte los ejemplos a continuación para obtener más detalles.

3.2 Parámetros de ejecución

  • -v muestra información de prueba detallada: pytest.main (['-v']), que muestra en detalle el nombre del método de cada caso de uso, los resultados, el progreso y otra información.

Insertar descripción de la imagen aquí

  • -s muestra información de impresión en el caso de uso: pytest.main(['-s'])

  • -q Salida silenciosa, solo muestra los resultados de la operación:pytest.main(['-q', '../Testcase/test_pytest_demo.py'])

  • -n Caso de prueba de ejecución de subprocesos múltiples: pytest.main(['-vs', '../Testcase/test_pytest_demo.py','-n2'])el número después de n es el número de subprocesos, debe instalar el complemento pytest-xdist antes de usarlo

  • -m ejecuta el caso de uso de la marca especificada: pytest.main (['-m markName']). El nombre de la marca personalizada se puede utilizar después de registrarlo en el archivo pytest.ini.

  • -lf vuelve a ejecutar el último caso de prueba fallido: pytest.main(['-v', '.../Testcase/test_pytest_demo.py', '-lf']), si no hubo ningún caso de prueba fallido la última vez, ejecute todos


  • –reruns=N Vuelve a ejecutar automáticamente los casos de prueba fallidos N veces:pytest.main(['-q', '--reruns=1', '../Testcase/test_pytest_demo.py'])

Insertar descripción de la imagen aquí

3.3 Alcance de ejecución

  • Ejecute todos los casos de uso: pytest.main()
  • Ejecute el caso de uso en el directorio especificado: pytest.main(['.../Testcase'])
  • Ejecute el caso de uso del módulo especificado: pytest.main(['…/Testcase/test_pytest_demo.py'])
  • Ejecute el caso de uso de la clase especificada: pytest.main(['.../Testcase/test_pytest_demo.py::TestPytest'])
  • Caso de uso para ejecutar el método especificado: pytest.main(['.../Testcase/test_pytest_demo.py::TestPytest::test_001'])
    Además, también puede configurar las reglas de búsqueda al ejecutar el caso de uso a través de pytest. inicio, ejemplo:
[pytest]
testpaths = ../TestCase
python_files =  test_*.py
python_classes = Test*
python_functions = test_*

3.4 Dispositivo de fijación

Los decoradores @pytest.fixturese pueden utilizar para establecer condiciones previas y posteriores para la ejecución de casos de uso. En el método decorado, las funciones de condición previa y posterior se pueden realizar mediante el uso de rendimiento. El rendimiento tiene la función de retorno de retorno, pero el código después del rendimiento se puede ejecutar (si la función A tiene rendimiento, se considerará como una función generada cuando se ejecuta. Cuando la función A alcanza el rendimiento, saltará de la función A y ejecutará el segmento de código B que llama a la función A. Después de ejecutar el segmento de código B, continuará regresando a la función A para ejecutar el código después del rendimiento. ).

Método de configuración del accesorio:

@pytest.fixture(scope=scope, params=Iterable[object], autouse=True, ids=Iterable[object], name=fixtureName)
  • alcance: alcance

    • función: alcance predeterminado, nivel de caso de uso, destruir el dispositivo de carpeta al final del caso de prueba (es decir, el código después de ejecutar rendimiento);
    • clase: Actúa sobre una clase. El dispositivo se destruye cuando finaliza la última prueba de la clase;
    • módulo: actúa sobre el módulo (archivo .py), el último dispositivo de destrucción de prueba en el módulo;
    • paquete: actúa sobre el paquete python, el último dispositivo de destrucción de prueba del paquete;
    • Sesión: Actúa sobre la sesión. La última prueba de la sesión destruye el dispositivo.
  • params: Parámetros, el formato es una tupla o lista. El número de parámetros determinará el número de ejecuciones. El método decorado necesita heredar la solicitud (solicitudes que no son de interfaz), use request.param para obtener los parámetros, uno a la vez;

  • uso automático: llama automáticamente a la configuración, Verdadero/Falso, el valor predeterminado es Falso;

  • ids: ID de prueba, el formato es tupla o lista;

  • nombre: el nombre del dispositivo se puede configurar a través del nombre. Después de la configuración, el nombre debe ingresarse al llamar.

Método de llamada del accesorio:

  • Método 1: pasarlo directamente como parámetro a la llamada del caso de prueba
  • Método 2: llamar a través del decorador@pytest.mark.usefixtures('fixtureName1', 'fixtureName2')
  • Método 3: al configurar el dispositivo, configure Llamada automática en Verdadero@pytest.fixture(autouse=True)

Código de muestra para configurar y llamar dispositivos:

"""
   Pytest fixture装置使用示例
"""
import pytest

@pytest.fixture
def human():
    """未设置参数,默认为用例级,谁调用谁享用"""
    name = '小明'
    print('前置条件:小明醒了,未设置参数,谁调用谁享用')
    yield name
    print('后置条件:小明睡了,未设置参数,谁调用谁享用')

@pytest.fixture(autouse=True)
def weather():
    """作用范围:默认为用例级,设置自动调用为True,每个用例都会调用"""
    sun = '晴天'
    print('前置条件:天亮了autouse=True,默认作用每个用例')
    yield sun
    print('后置条件:天黑了autouse=True,默认作用每个用例')

@pytest.fixture(scope='class')
def time():
    """作用范围:类级,谁调用谁享用"""
    t1 = '6:00'
    t2 = '20:00'
    print('前置条件:早上好class')
    yield t1, t2
    print('后置条件:晚上好class')

@pytest.fixture(scope='session', autouse=True)
def session():
    """作用范围:会话级,yield后可不写内容,不返回数据,设置自动调用为True"""
    print('前置条件:会话开始-----------------')
    yield
    print('后置条件:会话结束-----------------')

@pytest.fixture(params=['小美', '小帅'], ids=('mei', 'shuai'), name='clm')
def params_classmate(request):
    """作用范围:默认用例级,谁调用谁享用params和ids"""
    param = request.param
    print(f'前置条件:{param}')
    yield param
    print(f'后置条件:{param}')

class TestOneDay1:
    """调用方式一,直接作为参数传给测试用例:human,time,weather,clm"""
    def test_eat(self, human, time, clm):
        t1, t2 = time
        print(human + t1 + f'和{clm}吃了早餐')
    def test_school(self, weather, human):
        print('今天是'+weather+','+human + '去上学了')

@pytest.mark.usefixtures('human','time')
class TestOneDay2:
    """调用方式二:通过装饰器调用'human'和'time',但无法传递参数"""
    def test_homework(self):
        print('交了作业')
    def test_home(self):
        print('放学独自回家')

class TestOneDay3:
    """没有直接调用fixture,但设置了自动调用的会被调用"""
    def test_water(self):
        print('喝了水')
    def test_wc(self):
        print('去了wc')

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

resultado de la operación:

============================= test session starts =============================
collecting ... collected 7 items

test_pytest_fixture_demo.py::TestOneDay1::test_eat[mei] 前置条件:会话开始-----------------
前置条件:早上好class
前置条件:天亮了autouse=True,默认作用每个用例
前置条件:小明醒了,未设置参数,谁调用谁享用
前置条件:小美
PASSED           [ 14%]小明6:00和小美吃了早餐
后置条件:小美
后置条件:小明睡了,未设置参数,谁调用谁享用
后置条件:天黑了autouse=True,默认作用每个用例

test_pytest_fixture_demo.py::TestOneDay1::test_eat[shuai] 前置条件:天亮了autouse=True,默认作用每个用例
前置条件:小明醒了,未设置参数,谁调用谁享用
前置条件:小帅
PASSED         [ 28%]小明6:00和小帅吃了早餐
后置条件:小帅
后置条件:小明睡了,未设置参数,谁调用谁享用
后置条件:天黑了autouse=True,默认作用每个用例

test_pytest_fixture_demo.py::TestOneDay1::test_school 前置条件:天亮了autouse=True,默认作用每个用例
前置条件:小明醒了,未设置参数,谁调用谁享用
PASSED             [ 42%]今天是晴天,小明去上学了
后置条件:小明睡了,未设置参数,谁调用谁享用
后置条件:天黑了autouse=True,默认作用每个用例
后置条件:晚上好class

test_pytest_fixture_demo.py::TestOneDay2::test_homework 前置条件:早上好class
前置条件:天亮了autouse=True,默认作用每个用例
前置条件:小明醒了,未设置参数,谁调用谁享用
PASSED           [ 57%]交了作业
后置条件:小明睡了,未设置参数,谁调用谁享用
后置条件:天黑了autouse=True,默认作用每个用例

test_pytest_fixture_demo.py::TestOneDay2::test_home 前置条件:天亮了autouse=True,默认作用每个用例
前置条件:小明醒了,未设置参数,谁调用谁享用
PASSED               [ 71%]放学独自回家
后置条件:小明睡了,未设置参数,谁调用谁享用
后置条件:天黑了autouse=True,默认作用每个用例
后置条件:晚上好class

test_pytest_fixture_demo.py::TestOneDay3::test_water 前置条件:天亮了autouse=True,默认作用每个用例
PASSED              [ 85%]喝了水
后置条件:天黑了autouse=True,默认作用每个用例

test_pytest_fixture_demo.py::TestOneDay3::test_wc 前置条件:天亮了autouse=True,默认作用每个用例
PASSED                 [100%]去了wc
后置条件:天黑了autouse=True,默认作用每个用例
后置条件:会话结束-----------------


============================== 7 passed in 0.02s ==============================

进程已结束,退出代码0

En el ejemplo anterior, los métodos y casos de prueba bajo el dispositivo de fijación están escritos en el mismo archivo, pero en aplicaciones reales, el contenido debe escribirse en un archivo py separado. En este caso, puede utilizar la ayuda de conftest. .py, administre mejor estos métodos de instalación de dispositivos (casos de uso previos y posteriores a las condiciones).

El archivo conftest.py es una forma de proporcionar dispositivos para todo el directorio. El dispositivo de dispositivo se puede escribir en el archivo. No es necesario importarlo cuando se usa. Al ejecutar el caso de prueba, pytest descubrirá automáticamente el archivo. Pero debe tenerse en cuenta que el alcance de conftest.py son todos los casos de prueba en el directorio y subdirectorios donde se encuentra (por ejemplo, todos los casos de prueba están en el directorio TestCase, entonces el archivo conftest.py debe colocarse en el Directorio de casos de prueba)

Código de muestra de conftest.py:

"""
测试conftest.py文件
"""
import pytest

@pytest.fixture(autouse=True)
def test_conftest():
    print("conftest.py中的前置条件")
    yield
    print("conftest.py中的后置条件")

Código de caso de prueba:

"""
   conftest.py文件使用
"""
import pytest

class TestPytest():
    """测试Pytest类"""

    def test_001(self):
        """test_001"""
        print("test_001")
        a = "a"
        assert a == a

    def test_002(self):
        """test_002"""
        print("test_002")
        assert 1 < 2

resultado de la operación:

..\Testcase\test_pytest_demo.py::TestPytest::test_001 conftest.py中的前置条件
test_001
PASSEDconftest.py中的后置条件
..\Testcase\test_pytest_demo.py::TestPytest::test_002 conftest.py中的前置条件
test_002
PASSEDconftest.py中的后置条件
============================== 2 passed in 0.01s ==============================
进程已结束,退出代码0

3.5 Parametrización

A través del decorador @pytest.mark.parametrize ('paramNameN', 'paramValue'), los datos de prueba se pueden parametrizar para lograr propósitos basados ​​en datos. El nombre del parámetro se pasa directamente en forma de cadena y el valor del parámetro se puede ser una tupla, listas, funciones (las funciones personalizadas deben pasarse en forma de lista), etc. Las pruebas basadas en datos también se pueden implementar combinándolas con archivos csv y yaml.
Código de ejemplo parametrizado:

"""
   Pytest @pytest.mark.parametrize参数化装饰器使用示例
"""
import random
import pytest


def random_num():
    return random.randint(88888888, 99999999)

class TestParametrize():
    """@pytest.mark.parametrize参数化装饰器"""

    @pytest.mark.parametrize('name', ('小明', '小帅', '小黑'))
    def test_001(self, name):
        """1个参数"""
        print(f"{name}")
        assert 1 == 1

    @pytest.mark.parametrize('name, age', [('小明', '18'), ('小帅', '17'), ('小黑', '16')])
    def test_002(self, name, age):
        """多个参数"""
        print(f"{name}:{age}")
        assert 1 == 1

    @pytest.mark.parametrize('caseinfo', [{'name': '小明', 'age': '18', 'except': 'true'},
                                          {'name': '小美', 'age': '17', 'except': 'true'}])
    def test_003(self, caseinfo):
        """以列表字典形式传入多组测试数据"""
        name = caseinfo['name']
        age = caseinfo['age']
        print(f"{name}:{age}")
        assert caseinfo['except'] == 'true'

    @pytest.mark.parametrize('num', [random_num()])
    def test_004(self, num):
        """传入随机数"""
        print(f"中奖号码为:{num}")
        assert 1 == 1

    @pytest.mark.parametrize('num', range(5))
    def test_005(self, num):
        """传入一个range(函数)"""
        print(f"序号:{num}")
        assert 1 == 1

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

resultado de la operación:

============================= test session starts =============================
collecting ... collected 14 items
test_pytest_mark_parametrize.py::TestParametrize::test_001[\u5c0f\u660e] PASSED [  7%]小明
test_pytest_mark_parametrize.py::TestParametrize::test_001[\u5c0f\u5e05] PASSED [ 14%]小帅
test_pytest_mark_parametrize.py::TestParametrize::test_001[\u5c0f\u9ed1] PASSED [ 21%]小黑
test_pytest_mark_parametrize.py::TestParametrize::test_002[\u5c0f\u660e-18] PASSED [ 28%]小明:18
test_pytest_mark_parametrize.py::TestParametrize::test_002[\u5c0f\u5e05-17] PASSED [ 35%]小帅:17
test_pytest_mark_parametrize.py::TestParametrize::test_002[\u5c0f\u9ed1-16] PASSED [ 42%]小黑:16
test_pytest_mark_parametrize.py::TestParametrize::test_003[caseinfo0] PASSED [ 50%]小明:18
test_pytest_mark_parametrize.py::TestParametrize::test_003[caseinfo1] PASSED [ 57%]小美:17
test_pytest_mark_parametrize.py::TestParametrize::test_004[92025408] PASSED [ 64%]中奖号码为:92025408
test_pytest_mark_parametrize.py::TestParametrize::test_005[0] PASSED     [ 71%]序号:0
test_pytest_mark_parametrize.py::TestParametrize::test_005[1] PASSED     [ 78%]序号:1
test_pytest_mark_parametrize.py::TestParametrize::test_005[2] PASSED     [ 85%]序号:2
test_pytest_mark_parametrize.py::TestParametrize::test_005[3] PASSED     [ 92%]序号:3
test_pytest_mark_parametrize.py::TestParametrize::test_005[4] PASSED     [100%]序号:4

============================= 14 passed in 0.02s ==============================

进程已结束,退出代码0

3.6 Registro

Al configurar los parámetros relacionados con el registro en pytest.ini, se puede realizar la función de registro Parámetros configurables:

  • log_level: capturar/mostrar nivel de registro, CRÍTICO > ERROR > ADVERTENCIA > INFORMACIÓN > DEPURACIÓN > NOTSET, ADVERTENCIA predeterminada;
  • log_format: formato de registro;
  • log_date_format: formato de hora de registro;
  • log_cli_level: nivel de registro en tiempo real;
  • log_cli_format: formato de registro en tiempo real;
  • log_cli_date_format: formato de tiempo de registro en tiempo real;
  • log_file: ruta del archivo de escritura de registro (sobrescritura);
  • log_file_level: el nivel de registro escrito en el archivo de registro;
  • log_file_format: formato de registro escrito en el archivo de registro;
  • log_file_date_format: el formato de hora escrito en el archivo de registro;
  • log_auto_indent: sangra automáticamente los mensajes de varias líneas pasados ​​al módulo de registro, acepta verdadero/activado, falso/desactivado o entero.

Ejemplo de registro de configuración de pytest.ini:

[pytest]
addopts = -vs --color=yes
log_cli = true

log_level = DEBUG
log_format = %(asctime)s %(filename)-s:%(lineno)-s %(levelname)-8s - %(message)s
log_date_format = %Y/%m/%d %H:%M:%S
log_auto_indent = true

log_file = ../Log/logs.log
log_file_level = DEBUG
log_file_format = %(asctime)s %(filename)-s:%(lineno)-s %(levelname)-8s - %(message)s
log_file_date_format = %Y/%m/%d %H:%M:%S

Código de muestra de registro:

"""
   Pytest logging使用示例
"""
from time import sleep
import logging
logger = logging.getLogger('__name__')

def test_pytest_log():
    logger.info('info msg')
    logger.debug('debug msg')
    logger.error('error msg')
    sleep(3)
    logger.critical('critical msg')
    sleep(3)
    logger.warning('warning msg')
    logger.info('info \n多行缩进\n多行缩进\n多行缩进')

if __name__ == '__main__':
    test_pytest_log()

Resultados de ejecución - consola:


Resultados de ejecución - archivo de registro:

Insertar descripción de la imagen aquí

3.7 Afirmaciones

Puede utilizar directamente las afirmaciones que vienen con Python. El formulario de presentación específico se puede ver en el código de muestra anterior.

  • afirmar a en b: afirmar que a contiene b
  • afirmar a == b: afirmar que a es igual a b
  • afirmar 1 < 2: afirmar que 1 es menor que 2
  • ……

3.8 Generar informe de prueba

Método 1: instale el complemento de terceros pytest-allure. Para un uso detallado, consulte la aplicación básica de Allure en el marco de pruebas automatizadas de Pytest.


Método 2: instale el complemento de terceros pytest-html, agregue --html=../report/report.htmlparámetros para generar un informe html
y ejecutarlo .pytest.main(['-vs', '--html=../report/report.html', '../Testcase/test_pytest_report_html.py'])

Insertar descripción de la imagen aquí
Nota: Este artículo es una nota de estudio, que incluye errores, optimizaciones, etc. Bienvenido a comunicarse y corregir.


Finalmente, compartiré con ustedes los documentos y materiales de aprendizaje que he acumulado y que son veraces. Si es necesario, pueden recogerlos. El contenido anterior debería ser el almacén de preparación más completo y completo

para los amigos de pruebas de software. Para poder mejorar organizarlo Para cada módulo, también me referí a muchas publicaciones de blogs y proyectos de alta calidad en Internet, tratando de no perder ningún punto de conocimiento. Muchos amigos confiaron en estos contenidos para revisar y obtuvieron ofertas de los principales fabricantes como BATJ. También me ha ayudado mucho. Como estudiante de pruebas de software, espero que también pueda ayudarte.

¡Siga mi cuenta oficial de WeChat a continuación para obtenerla gratis! ↓ ↓ ↓ ↓ ↓

Supongo que te gusta

Origin blog.csdn.net/weixin_56331124/article/details/132766568
Recomendado
Clasificación