Marco de prueba de Pytest impulsado por datos

Introducción

Les he presentado el DDT, el marco basado en datos del marco de prueba Unittest y sus principios de implementación. Lo que comparto con ustedes hoy es la unidad de datos del marco de prueba Pytest. La unidad de datos del marco de prueba Pytest es implementada por el pytest.mark.parametrize () que viene con pytest.

 

pytest.mark.parametrize implementa datos basados ​​en

pytest.mark.parametrize es un decorador incorporado de pytest, que le permite definir múltiples conjuntos de parámetros y accesorios en función o clase para lograr resultados basados ​​en datos.

El decorador @ pytest.mark.parametrize () acepta dos parámetros: el
primer parámetro existe en forma de cadena, que representa los parámetros que pueden ser aceptados por la función bajo prueba. Si la función bajo prueba tiene múltiples parámetros, es Separe por comas; el
segundo parámetro se usa para guardar los datos de prueba. Si solo hay un conjunto de datos, existe en forma de lista. Si hay varios conjuntos de datos, existe en forma de tuplas anidadas de listas (por ejemplo: [1,1] o [(1, 1), (2,2)]).

 

Para el parámetro único y múltiples parámetros del decorador, los ejemplos son los siguientes.

1.pytest.mark.parametrize único parámetro

# test_singal.py
import pytest

@pytest.mark.parametrize("number", [1, 0])
def test_equal(number):
    assert number == 1


if __name__ == "__main__":
    pytest.main([])

Lo anterior es un ejemplo de un solo parámetro. En este ejemplo, la función test_equal recibe un número de parámetro. Este parámetro tiene dos conjuntos de datos, 1 y 0.

consejos:

El nombre del parámetro en el primer parámetro del decorador pytest.mark.parametrize debe ser coherente con el nombre del parámetro en la función de prueba.
Es decir: el número de parámetro del método de función test_equal debe ser coherente con el número de nombre del primer parámetro en el decorador.

Al ejecutar el código anterior, los resultados se muestran a continuación:
imagen
se puede ver, proporciona dos funciones test_equal parámetros 0 y 1, también se realizó dos veces.

 

2.pytest.mark.parametrize multiparámetro

pytest.mark.parametrize admite no solo un único parámetro, sino también múltiples parámetros. Los múltiples parámetros son más comunes, porque en nuestro trabajo diario, proporcionamos datos de prueba, no solo datos para pruebas, sino también datos para verificación, por lo que se utilizan múltiples parámetros relativamente comun.

pytest.mark.parametrize puede admitir múltiples parámetros, por ejemplo:

# test_baidu.py

import time
import pytest
from selenium import webdriver

@pytest.mark.baidu
class TestBaidu:
    def setup_method(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(30)
        self.base_url = "http://www.baidu.com/"

    @pytest.mark.parametrize('search_string, expect_string', [('Testing', 'Testing'), ('helloworld.com', 'Testing')])
    def test_baidu_search(self, search_string, expect_string):
        driver = self.driver
        driver.get(self.base_url + "/")
        driver.find_element_by_id("kw").send_keys(search_string)
        driver.find_element_by_id("su").click()
        time.sleep(2)
        search_results = driver.find_element_by_xpath('//*[@id="1"]/h3/a').get_attribute('innerHTML')
        assert (expect_string in search_results) is True

    def teardown_method(self):
        self.driver.quit()


if __name__ == "__main__":
    pytest.main(["-m", "baidu", "-s", "-v", "-k", "test_baidu_search", "test_baidu.py"])

En el código anterior, la función probada test_baidu_search tiene dos parámetros, cadena_búsqueda y cadena_esperada. Por tanto, el primer parámetro del decorador pytest.mark.parametrize también contiene search_string y wait_string.

Los resultados de la ejecución en la línea de comandos son los siguientes:
imagen

 

unidad de datos extendida pytest.fixture

Los amigos que han realizado pruebas automatizadas deben saber que las pruebas automatizadas tanto de la API como de la IU se pueden resumir en tres pasos:
preparación antes de la prueba -> realización de la prueba -> limpieza después de la prueba.

En las pruebas diarias, la preparación antes de la prueba suele ser la condición previa requerida para la prueba. Puede ser una operación de inicio de sesión simple, una operación de base de datos de consulta conjunta, una operación de preparación de lectura de datos de prueba o incluso una operación de función lógicamente compleja.

Al igual que el marco unittest, en pytest, también puede usar la configuración y el desmontaje para completar el trabajo previo a la prueba.
P.ej:

  • Utilice setup_method, setup_class y setup_module para completar la preparación del método de prueba, la clase de prueba y el módulo de prueba respectivamente;

  • Utilice teardown_method, teardown_class, teardown_module para completar el método de la clase de prueba, la clase de prueba y las operaciones de limpieza del módulo de prueba, respectivamente.

 

Pero este método tiene un defecto relativamente obvio. 
Por ejemplo, en la misma clase de prueba, hay varios métodos de prueba. Suponiendo que cada método de prueba requiere una función de configuración o desmontaje diferente, ¿qué debemos hacer?

Por otro ejemplo, la configuración y el desmontaje son en realidad dispositivos de prueba (dispositivos de prueba). Si quiero administrar todos los dispositivos de prueba en una función, ¿puedo hacerlo?

 

Pytest tiene en cuenta esta situación y proporciona una función más avanzada, que es el decorador de luminarias.

Los dispositivos se pueden utilizar para inicializar los servicios de prueba, los datos y el estado, y a menudo se utilizan para realizar operaciones previas o posteriores a la prueba antes o después de la ejecución de la prueba. 
Los dispositivos se pueden usar como datos compartidos y también pueden ser llamados por otras funciones, módulos, clases o todo el proyecto, o incluso otros dispositivos.

 

1.sintaxis de accesorios

La sintaxis de pytest.fixtures es la siguiente:

fixture(scope="function", params=None, autouse=False, ids=None, name=None)

En la sintaxis, puede ver que los cinco parámetros del dispositivo son los siguientes:

alcance: utilizado para controlar el alcance del dispositivo.
Este parámetro tiene los siguientes 4 niveles:

  • función: se llamará en cada función o método de clase (predeterminado).

  • clase: se llama solo una vez en cada clase.

  • módulo: Cada archivo .py se llama una vez; puede haber múltiples funciones y clases en el archivo.

  • sesión: una sesión se llama una vez.

 

params:
existe una lista de parámetros opcionales params en forma de lista de parámetros opcionales. Cuando se usa en una función de prueba, puede recibir el valor de retorno establecido (es decir, el valor en la lista de parámetros) a través de request.param. Cuántos elementos hay en los parámetros, la función que hace referencia a este dispositivo se llamará varias veces durante la prueba.

 

autouse: si se ejecutarán automáticamente los dispositivos configurados.Cuando
autouse es Verdadero, incluso si la función de prueba no llama al decorador de dispositivos, se ejecutará la función de dispositivo definida.

 

ids: especifique cada ID de cadena
cuando haya varios parámetros, para cada parámetro, puede especificar el ID, este ID se convertirá en parte del nombre del caso de prueba. Si no se proporciona una identificación, la identificación se generará automáticamente.

 

name: El nombre del nombre de la luminaria
es el nombre de las luminarias, y por defecto es el nombre de la función de luminaria que decoras. Puedes cambiar el nombre de este dispositivo a través del parámetro de nombre. Después del cambio, si se llama a este dispositivo, simplemente usa el nombre que cambiaste.

 

2.uso de accesorios

Hay muchas formas de utilizar los dispositivos, a continuación se ofrecen algunos ejemplos.

(1), utilizar directamente a través del nombre de la función del dispositivo

#test_fixture_usage.py

import pytest
# 首先, 在fixture函数上,加@pytest.fixture()

@pytest.fixture()
def my_method():
    print('This is testing fixture')

# 其次,把fixture函数的函数名作为参数,传入被测试用例

def test_use_fixtures(my_method):
    print('Please follow Testing from WL')

Los pasos para usar el dispositivo a través del nombre de la función del dispositivo son:

  • En la función de accesorio, agregue @ pytest.fixture (), el método my_method en el ejemplo anterior se usará como accesorio;

  • Tome el nombre de la función de la función de fijación como parámetro y páselo al caso probado. 
    Nota: El parámetro de entrada de la función test_use_fixtures debe ser el nombre del método my_method, que es coherente con la función fixture.

Al ejecutar el código anterior, en el resultado de la ejecución, encontrará que my_method, el método del dispositivo definido, se ejecuta antes que las otras declaraciones de la función de prueba (equivalente a la función de configuración).

 

(2). Usando el decorador de
accesorios de uso . Al usar el dispositivo como una función de prueba como parámetro, puede configurar diferentes funciones de instalación y desmontaje para cada función de prueba, pero esto unirá el dispositivo y mi función de prueba juntos. No es propicio para la reutilización de funciones de prueba y la estructura clara del marco de prueba.

Por lo tanto, pytest proporciona el decorador pytest.mark.usefixtures.
El siguiente código ilustra el uso específico de los accesorios de uso:

#test_fixture_usage.py

import pytest

@pytest.fixture()
def my_method():
    print('This is Testing fixture')

# 函数直接使用fixture
@pytest.mark.usefixtures('my_method')
def test_use_fixtures():
    print('Please follow Testing from WL')

class TestClass1:
    # 类方法使用fixture
    @pytest.mark.usefixtures('my_method')
    def test_class_method_usage(self):
        print('[classMethod]Please follow Testing from WL')



# 类直接使用fixture
@pytest.mark.usefixtures('my_method')
class TestClass2:
    def test_method_usage_01(self):
        pass

    def test_method_usage_02(self):
        pass

Como puede ver en este código, las funciones, métodos de clase y clases pueden llamar a usefixtures.

 

(3) Uso de múltiples parámetros de dispositivos El
método de uso mencionado anteriormente permite que diferentes funciones de prueba llamen a diferentes dispositivos de prueba, entonces, ¿qué debemos hacer si nuestro dispositivo toma parámetros? Consulte el siguiente código:

import pytest


@pytest.fixture(params=['hello', 'Testing'])
def my_method(request):
    return request.param

def test_use_fixtures_01(my_method):
    print('this is the first test')
    print(my_method)


@pytest.mark.usefixtures('my_method')
def test_use_fixtures_02():
    print('this is the second test')

    # 注意,如果在这里想通过print(my_mthod)来打印出fixuture提供的参数,是不行的, 因为使用usefixtures无法获取fixture的返回值,如需要fixture的返回值,则需用test_use_fixtures_01那样的调用方式

Ejecute este código, verá que se ejecutan 4 casos de prueba. Se puede ver que pytest se basa en datos a través de accesorios y sus parámetros de parámetros.

 

(4) Uso implícito a través de parámetros de uso automático El
método anterior se da cuenta del acoplamiento flojo de dispositivos y funciones de prueba, pero todavía hay un problema: cada función de prueba necesita declarar explícitamente qué dispositivos usar.

En base a esto, pytest proporciona el parámetro autouse, que nos permite usar el dispositivo definido sin llamar al decorador del dispositivo. Vea el siguiente ejemplo:

#test_fixture_usage.py

import pytest

@pytest.fixture(params=['hello', 'Testing'], autouse=True, ids=['test1', 'test2'], name='test')
def my_method(request):
    print(request.param)

def test_use_fixtures_01():
    print('this is the first test')

def test_use_fixtures_02():
    print('this is the second test')

Al ejecutar el código anterior y usar allure [allure cómo generar un enlace de tweet de informe de prueba], los resultados de generar el informe de prueba son los siguientes:
imagenimagen

Cuando la función fixture está definida y autouse es True, no hay necesidad de declarar explícitamente el fixture que se utilizará en la función de prueba (en este ejemplo, no puede ver que el fixture my_method se llama explícitamente en el método de prueba). El accesorio definido estará dentro del alcance especificado por pytest.fixtures, y el accesorio se aplicará a cada función de prueba debajo de él.

En este ejemplo, el parámetro de alcance no está definido y se usará el valor predeterminado "función", es decir, se ejecutará cada función de prueba, y nuestros parámetros proporcionan dos conjuntos de parámetros, por lo que se ejecutan un total de 4 casos de prueba. .

Preste atención al nombre del caso de prueba. Para cada caso de prueba, debido a que @ pytest.fixture especifica los identificadores como ['prueba1', 'prueba2'], el nombre del caso de prueba también incluye el identificador especificado.

 

(5) Uso del producto cartesiano
de múltiples luminarias Cuando tenga múltiples luminarias que necesiten usarse superpuestas, puede usarlas superpuestas. Nota: Este método organizará los distintos grupos de parámetros de fijación en forma de producto cartesiano, tomando el siguiente código como ejemplo, la ejecución generará 4 casos de prueba.

import pytest

class TestClass:
    @pytest.fixture(params=['hello', 'Testing'], autouse=True)
    def my_method1(self, request):
        print('the param are:{}'.format(request.param))
        return request.param

    @pytest.fixture(params=['world', 'is good'], autouse=True)
    def my_method2(self, request):
        print('the param are:{}'.format(request.param))
        return request.param

    def test_use_fixtures_01(self):
        pass

 

(6) Use conftest.py para compartir dispositivos.
A través de los ejemplos anteriores, todos deberían haber dominado cómo definir, compartir y usar dispositivos en el mismo archivo. Pero en las pruebas de trabajo diarias, a menudo necesitamos utilizar la misma operación de prueba previa en el alcance global.


Por ejemplo: al comienzo de la prueba, inicie sesión primero y luego conéctese a la base de datos y otras operaciones.

En este caso, necesitamos usar conftest.py. Los dispositivos definidos en conftest.py no necesitan ser importados, pytest los encontrará y usará automáticamente. El orden en el que pytest encuentra los accesorios es encontrar primero la clase de prueba (Clase), luego el módulo de prueba (Módulo), luego el archivo conftest.py y finalmente los complementos integrados o de terceros.

 

Veamos cómo usar  conftest.py

Suponga que la estructura del directorio es la siguiente:

|--APITest

    |--tests

        |--test_fixture1.py

        |--test_baidu_fixture_sample.py

        |--conftest.py

        |--__init__.py

 

El código de conftest.py es el siguiente:

# conftest.py

import pytest
import requests
from selenium import webdriver


@pytest.fixture(scope="session")
# 此方法名可以是你登录的业务代码,也可以是其他,这里暂命名为login
def login():
    driver = webdriver.Chrome()
    driver.implicitly_wait(30)
    base_url = "http://www.baidu.com/"
    s = requests.Session()
    
    yield driver, s, base_url
    print('turn off browser driver')
    driver.quit()
    print('turn off requests driver')

    s.close()


@pytest.fixture(scope="function", autouse=True)
def connect_db():
    print('connecting db')
    # 此处写你的连接db的业务逻辑
    pass

 

El código de test_fixture1.py es el siguiente:

# test_fixture1.py

import pytest

class TestClass:
    def test_use_fixtures_01(self, login):
        print('I am data:{}'.format(login))

 

El código de test_baidu_fixture_sample.py es el siguiente:

import time
import pytest


@pytest.mark.baidu
class TestBaidu:

    @pytest.mark.parametrize('search_string, expect_string', [('Testing', 'Testing'), ('helloworld.com', 'Testing')])
    def test_baidu_search(self, login, search_string, expect_string):
        driver, s, base_url = login
        driver.get(base_url + "/")
        driver.find_element_by_id("kw").send_keys(search_string)
        driver.find_element_by_id("su").click()
        time.sleep(2)

        search_results = driver.find_element_by_xpath('//*[@id="1"]/h3/a').get_attribute('innerHTML')
        print(search_results)
        assert (expect_string in search_results) is True


if __name__ == "__main__":
    pytest.main([])


Ejecute el siguiente código en la línea de comando: D: \ Auto \ APITest> pytest -s -q --tb = no tests --alluredir =. / Allure_reports

Una vez completada la ejecución de la prueba, verifique el resultado de la ejecución:
imagenimagen

Se puede observar en la figura anterior que la declaración que conecta db se imprime tres veces porque el alcance del accesorio connect_db está configurado para funcionar en conftest.py y el valor del atributo de autouse es Verdadero. Las instrucciones de desactivar el controlador del navegador y desactivar las solicitudes se ejecutaron solo una vez, porque el alcance del dispositivo de inicio de sesión es la sesión, por lo que se ejecutó solo una vez en toda la sesión.

 

Además, tenga en cuenta que en el inicio de sesión del dispositivo, hay las siguientes declaraciones:

...
...

yield driver, s, base_url
print('turn off browser driver')
driver.quit()

print('turn off requests driver')
s.close()

¿Qué significa esto? En el accesorio de pytest, la instrucción antes de la palabra clave yield pertenece a la configuración, y la instrucción después de la producción pertenece a la eliminación.

Para que pueda comprender por qué se ejecuta la siguiente declaración en último lugar:

print('turn off browser driver')
driver.quit()
print('turn off requests driver')
s.close()

 

Combine pytest.mark.parametrize y pytest.fixture

A través de la explicación anterior, aprendimos que en pytest, el decorador pytest.mark.parametrize se puede usar para pruebas basadas en datos, y el decorador pytest.fixture se puede usar para pruebas de configuración, desmontaje y uso compartido de dispositivos.

¿Qué efecto se puede lograr cuando se combinan pytest.mark.parametrize y pytest.fixture?

(1) Reduzca el código repetitivo y realice el intercambio de código global

Todas las funciones de prueba previa y posterior se pueden definir en el archivo conftest.py para toda la prueba, sin tener que definir en cada clase de prueba. Esto reduce en gran medida el código repetitivo, y conftest.py se define en el directorio raíz del proyecto y se puede aplicar globalmente, y cuando se define en una carpeta determinada, se puede aplicar a todos los archivos de prueba en esta carpeta.

 

(2), la prueba solo puede centrarse en la prueba en sí

La prueba solo se puede codificar en torno a su propio negocio, y se puede realizar usando conftest.py y pytest.fixture juntos. En una clase de prueba, solo se incluye el código de la prueba en sí, sin considerar la preparación antes de la prueba y el trabajo de limpieza después de la prueba.

 

(3) La migración del marco es más fácil

Si se trata de una prueba automatizada de IU, puede incluir todas las operaciones del controlador web en el archivo conftest.py. Si es una prueba de API, puede escribir todas las operaciones de solicitud de interfaz en el archivo conftest.py. De esta manera, cuando un nuevo proyecto necesita aplicar el marco de automatización, solo se deben cambiar los casos de prueba en la carpeta de pruebas.

Ejemplo de combinación de pytest.mark.parametrize y pytest.fixture:

# test_sample.py

import pytest


@pytest.fixture()
def is_odd(request):
    print('Now the parameter are:--{}\n'.format(request.param))
    if int(request.param) % 2 == 0:
        return False
    else:
        return True


@pytest.mark.parametrize("is_odd", [1, 0], indirect=True)
def test_is_odd(is_odd):
    if is_odd:
        print("is odd number")
    else:
        print("is not odd number")


if __name__ == "__main__":
    pytest.main([])

El código anterior define un método de fijación is_odd y un método basado en datos test_is_odd. Entre ellos, el método de fijación is_odd juzga si un número es impar o no; mientras que el método basado en datos test_is_odd proporciona un conjunto de datos y llama a la fijación is_odd para juzgar.

 

para resumir

El contenido compartido de hoy es cómo el marco de prueba de pytest se basa en datos, que se puede combinar con los decoradores pytest.mark.parametrize y pytest.fixture. Si aprende los diversos usos de pytest.mark.parametrize y pytest.fixture, podrá utilizar su marco de prueba libremente.

Bienvenido a prestar atención a la cuenta pública de [The Way of Infinite Testing], responder a [recibir recursos],
recursos de aprendizaje de programación de Python productos secos, automatización de la interfaz de usuario de la aplicación del marco Python + Appium , automatización de la interfaz de usuario web del marco Python + Selenium, API del marco Python + Unittest automatización,


Los recursos y códigos se envían gratis ~
Hay un código QR de la cuenta oficial en la parte inferior del artículo, puede escanearlo en WeChat y seguirlo.

Observaciones: Mi cuenta pública personal se ha abierto oficialmente, dedicada al intercambio de tecnología de prueba, que incluye: pruebas de big data, pruebas funcionales, desarrollo de pruebas, automatización de la interfaz API, operación y mantenimiento de pruebas, pruebas de automatización de la interfaz de usuario, etc., búsqueda pública de WeChat cuenta: "Wuliang The Way of Testing", o escanee el código QR a continuación:

 ¡Presta atención y crezcamos juntos!

Supongo que te gusta

Origin blog.csdn.net/weixin_41754309/article/details/113185895
Recomendado
Clasificación