¿Qué es BDD Testing? Marco de desarrollo basado en el comportamiento.

⚡ Resumen inteligente

Las pruebas BDD describen el comportamiento de la aplicación en un lenguaje simple de Dado-Cuando-Entonces para que analistas, desarrolladores y evaluadores compartan una especificación. Este tutorial aplica ese enfoque a las pruebas de API REST con Behave, la Python marco de trabajo que abarca la configuración, los archivos de características, las implementaciones de pasos, la ejecución y la generación de informes.

  • 🗣️ Principio básico: El desarrollo basado en el comportamiento (Behavior-Driven Development) amplía el desarrollo basado en pruebas (Test-Driven Development) al expresar cada escenario en un lenguaje natural que las personas que no son programadoras pueden leer y aprobar.
  • 🧱 Estructura del escenario: Given establece las condiciones previas, When realiza la acción y Then afirma el resultado, mientras que And o But heredan el decorador del paso precedente.
  • 🌐 Cobertura de REST: El ejemplo muestra el comportamiento CRUD completo mediante POST, GET, PUT y DELETE contra el servicio público de publicaciones jsonplaceholder.
  • 🐍 Configuración del marco: Behave se instala a través de pip en Python 3 y espera un directorio de características que contenga archivos de características más un paquete de pasos que contenga las implementaciones.
  • 🔗 Encuadernación por pasos: Los decoradores como @given, @when y @then coinciden con el texto de la característica, y los marcadores de posición entre llaves pasan valores del escenario al Python función.
  • ▶ ️ Ejecución: El formateador imprime en la consola un resultado de aprobado o reprobado con código de colores para cada paso.
  • 📊 Presentación de informes: El formateador allure-behave genera resultados legibles por máquina que la línea de comandos de Allure convierte en un informe HTML navegable.

¿Qué son las pruebas BDD?

¿Qué son las pruebas BDD (desarrollo impulsado por el comportamiento)?

Pruebas BDD (desarrollo impulsado por el comportamiento) BDD es una técnica de desarrollo de software ágil y una extensión de TDD, es decir, Desarrollo Dirigido por Pruebas. En BDD, los casos de prueba se escriben en un lenguaje natural que incluso personas sin conocimientos de programación pueden leer.

¿Cómo funcionan las pruebas BDD?

Imagina que te han asignado la tarea de crear un módulo de transferencia de fondos en una aplicación de banca en línea.

Existen varias formas de comprobarlo:

  1. La transferencia de fondos se realizará si hay saldo suficiente en la cuenta de origen.
  2. La transferencia de fondos debe realizarse si los detalles de la cuenta de destino son correctos
  3. La transferencia de fondos debería realizarse si la contraseña de la transacción / el código RSA / la autenticación de seguridad ingresada por el usuario son correctos.
  4. La transferencia de fondos debe realizarse incluso si es día festivo bancario.
  5. La transferencia de fondos debe realizarse en una fecha futura establecida por el titular de la cuenta.

El Escenario de prueba se vuelve más elaborado y complejo a medida que consideramos características adicionales como transferir la cantidad X durante un intervalo de Y días o meses, detenerping una transferencia programada cuando el total alcance Z, y así sucesivamente.

La tendencia general de los desarrolladores es desarrollar las funcionalidades y escribir el código de prueba más tarde. Como se evidencia en el caso anterior, Caso de prueba El desarrollo aquí es complejo, por lo que el desarrollador lo pospondrá. Pruebas hasta su lanzamiento, momento en el que se realizan pruebas rápidas pero ineficaces.

Para solucionar este problema, se concibió el Desarrollo Dirigido por Comportamiento (BDD, por sus siglas en inglés). Este método facilita todo el proceso de pruebas para el desarrollador.

En BDD, todo lo que escriba debe ir a Dado-cuando-entonces pasos. Consideremos el mismo ejemplo anterior en BDD:

Given that a fund transfer module in net banking application has been developed
And I am accessing it with proper authentication

When I shall transfer with enough balance in my source account
Or I shall transfer on a Bank Holiday
Or I shall transfer on a future date
And destination a/c details are correct
And transaction password / rsa code / security authentication for the transaction is correct
And press or click send button

Then amount must be transferred
And the event will be logged in log file

Este formulario es fácil de escribir, leer y comprender. Cubre todos los casos de prueba posibles para el módulo de transferencia de fondos y se puede modificar rápidamente para incluir más. Además, funciona como documentación viva del módulo. Dado que BDD surgió de TDD, conviene aclarar la diferencia entre ambos antes de pasar a las herramientas.

BDD vs TDD: Diferencias clave

Ambas prácticas se basan en el desarrollo mediante pruebas y ambas acortan los ciclos de retroalimentación. Se diferencian en quién escribe las pruebas, qué lenguaje utilizan y qué capa de la aplicación describen.

Aspecto TDD (Desarrollo Dirigido por Pruebas) BDD (desarrollo impulsado por el comportamiento)
Enfócate Cómo funciona internamente una unidad de código Cómo se comporta el sistema desde la perspectiva del usuario
Idioma aserciones del lenguaje de programación Escenarios de lenguaje natural Dado-Cuando-Entonces
Autor principal Developer Analista de negocios, propietario de producto, probador y desarrollador juntos
Readable por personas que no son programadoras No Sí:
Alcance típico Nivel de unidad Funcionalidad, API y nivel de aceptación
Herramientas comunes JUnit, pytest, NUnit Comportarse, Cucumber, SpecFlow, JBehave

Los dos son complementarios en lugar de competir. Los equipos suelen mantener TDD para el diseño a nivel de unidad y agregar escenarios BDD para describir el comportamiento que las partes interesadas aprueban. Dado que los puntos finales REST se ubican precisamente en esa capa de aceptación, son ideales para BDD.

¿Qué son las pruebas de API REST?

Dado que REST se ha convertido en un estilo popular para la creación de API, la automatización de los casos de prueba de API REST junto con los casos de prueba de interfaz de usuario se ha vuelto igualmente importante. Pruebas de API Consiste en probar las acciones CRUD (Crear-Leer-Actualizar-Eliminar) con los métodos POST, GET, PUT y DELETE respectivamente.

¿Qué es comportarse?

Comportarse es uno de los populares. Python Marcos de prueba BDD. Así es como funciona Behave:

  • Los archivos de características los redacta el analista de negocio, el patrocinador o la persona responsable de los escenarios de comportamiento. Un archivo de características utiliza un formato de lenguaje natural que describe una característica, o parte de ella, con ejemplos representativos de los resultados esperados.
  • Estos pasos del escenario se asignan a implementaciones de pasos escritas en Python.
  • Opcionalmente, los controles ambientales ejecutan código antes y después de los pasos, escenarios, funciones o de toda la ejecución.

Una vez definidas las funciones de cada archivo, ya se puede instalar el framework.

Configuración del marco de pruebas BDD Comportarse en Windows

Instalación:

Configuración del proyecto:

  • Crear un nuevo proyecto
  • Cree la siguiente estructura de directorios

Configuración del proyecto

La captura de pantalla anterior muestra el diseño que Behave espera: un Características directorio que contiene los archivos de características y un directorio anidado pasos directorio que contiene el Python implementaciones. Behave los detecta ambos por nombre, por lo que los nombres de las carpetas deben coincidir exactamente.

Archivos de características:

Ahora vamos a crear el archivo de características. Característica_muestra_REST_API_Testing., siendo la característica principal las operaciones CRUD en el servicio de 'publicaciones'.

Este ejemplo usa el https://jsonplaceholder.typicode.com/ Publica un servicio REST de ejemplo, una API simulada gratuita que acepta solicitudes de escritura y devuelve respuestas realistas sin guardar los cambios.

Ejemplo de escenario POST

Scenario: POST post example -> creating a new post item using the 'posts' service
Given I set post posts API endpoint          -> prerequisite: sets the URL of the posts service
When I set HEADER param request content type as "application/json"
And set request body
And send POST HTTP request                    -> the actual test step
Then I receive valid HTTP response code 201
And Response body "POST" is non-empty       -> verification of the response body

De forma similar, puedes escribir los escenarios restantes de la siguiente manera:

Configuración del proyecto

Característica_muestra_REST_API_Testing.

Feature: Test CRUD methods in Sample REST API testing framework

Background:
    Given I set sample REST API url

Scenario: POST post example
    Given I Set POST posts api endpoint
    When I Set HEADER param request content type as "application/json"
    And Set request Body
    And Send a POST HTTP request
    Then I receive valid HTTP response code 201
    And Response BODY "POST" is non-empty

Scenario: GET posts example
    Given I Set GET posts api endpoint "1"
    When I Set HEADER param request content type as "application/json"
    And Send GET HTTP request
    Then I receive valid HTTP response code 200 for "GET"
    And Response BODY "GET" is non-empty

Scenario: UPDATE posts example
    Given I Set PUT posts api endpoint for "1"
    When I Set Update request Body
    And Send PUT HTTP request
    Then I receive valid HTTP response code 200 for "PUT"
    And Response BODY "PUT" is non-empty

Scenario: DELETE posts example
    Given I Set DELETE posts api endpoint for "1"
    When I Send DELETE HTTP request
    Then I receive valid HTTP response code 200 for "DELETE"

Implementación de pasos

Ahora, para los pasos de características utilizados en los escenarios anteriores, puede escribir implementaciones en Python archivos dentro del directorio “steps”.

El marco Behave identifica la función de paso comparando los decoradores con el predicado del archivo de características. Por ejemplo, un Dado que los El predicado en un escenario de archivo de características busca una función escalonada que contenga la @given decorador. La misma coincidencia ocurre para Cuando y EntoncesEn el caso de 'But' y 'And', la función de paso toma el mismo decorador que su paso precedente. Por ejemplo, si 'And' sigue a un Given, el decorador de la función de paso correspondiente es @given.

Por ejemplo, el paso When para POST se puede implementar de la siguiente manera. Observe cómo "application/json" se pasa desde el archivo de características al marcador de posición entre llaves; esto se llama parametrización.

# Decorator: the braced name captures a value from the feature file
@when(u'I Set HEADER param request content type as "{header_content_type}"')
def step_impl(context, header_content_type):
    # Step implementation: set the content type on the request header
    request_headers['Content-Type'] = header_content_type

De manera similar, la implementación de los demás pasos en el paso Python El archivo se verá así:

Implementación de pasos

ejemplo_paso_implementación.py

from behave import given, when, then, step
import requests

api_endpoints = {}
request_headers = {}
response_codes = {}
response_texts = {}
request_bodies = {}
api_url = None

@given(u'I set sample REST API url')
def step_impl(context):
    global api_url
    api_url = 'https://jsonplaceholder.typicode.com'

# START POST Scenario
@given(u'I Set POST posts api endpoint')
def step_impl(context):
    api_endpoints['POST_URL'] = api_url + '/posts'
    print('url :' + api_endpoints['POST_URL'])

@when(u'I Set HEADER param request content type as "{header_content_type}"')
def step_impl(context, header_content_type):
    request_headers['Content-Type'] = header_content_type

# "And" or "But" steps are renamed by behave to match their preceding step
@when(u'Set request Body')
def step_impl(context):
    request_bodies['POST'] = {"title": "foo", "body": "bar", "userId": "1"}

@when(u'Send POST HTTP request')
def step_impl(context):
    # send the request and save the response object
    response = requests.post(url=api_endpoints['POST_URL'],
                             json=request_bodies['POST'],
                             headers=request_headers)
    response_texts['POST'] = response.text
    print("post response :" + response.text)
    response_codes['POST'] = response.status_code

@then(u'I receive valid HTTP response code 201')
def step_impl(context):
    print('Post rep code ;' + str(response_codes['POST']))
    assert response_codes['POST'] == 201
# END POST Scenario

# START GET Scenario
@given(u'I Set GET posts api endpoint "{id}"')
def step_impl(context, id):
    api_endpoints['GET_URL'] = api_url + '/posts/' + id
    print('url :' + api_endpoints['GET_URL'])

@when(u'Send GET HTTP request')
def step_impl(context):
    response = requests.get(url=api_endpoints['GET_URL'], headers=request_headers)
    response_texts['GET'] = response.text
    response_codes['GET'] = response.status_code

@then(u'I receive valid HTTP response code 200 for "{request_name}"')
def step_impl(context, request_name):
    print('Get rep code for ' + request_name + ':' + str(response_codes[request_name]))
    assert response_codes[request_name] == 200

@then(u'Response BODY "{request_name}" is non-empty')
def step_impl(context, request_name):
    print('request_name: ' + request_name)
    print(response_texts)
    assert response_texts[request_name] is not None
# END GET Scenario

# START PUT/UPDATE
@given(u'I Set PUT posts api endpoint for "{id}"')
def step_impl(context, id):
    api_endpoints['PUT_URL'] = api_url + '/posts/' + id
    print('url :' + api_endpoints['PUT_URL'])

@when(u'I Set Update request Body')
def step_impl(context):
    request_bodies['PUT'] = {"title": "foo", "body": "bar",
                             "userId": "1", "id": "1"}

@when(u'Send PUT HTTP request')
def step_impl(context):
    response = requests.put(url=api_endpoints['PUT_URL'],
                            json=request_bodies['PUT'],
                            headers=request_headers)
    response_texts['PUT'] = response.text
    print("update response :" + response.text)
    response_codes['PUT'] = response.status_code
# END PUT/UPDATE

# START DELETE
@given(u'I Set DELETE posts api endpoint for "{id}"')
def step_impl(context, id):
    api_endpoints['DELETE_URL'] = api_url + '/posts/' + id
    print('url :' + api_endpoints['DELETE_URL'])

@when(u'I Send DELETE HTTP request')
def step_impl(context):
    response = requests.delete(url=api_endpoints['DELETE_URL'])
    response_texts['DELETE'] = response.text
    print("DELETE response :" + response.text)
    response_codes['DELETE'] = response.status_code
# END DELETE

⚠️ Nota: Comparación de códigos de estado con == más bien que is asuntos. El is El operador prueba la identidad del objeto, no la igualdad, y Python 3.8 y luego plantear una SyntaxWarning: “is” con un literal para ese patrón. Cualquier afirmación escrita como assert code is 201 debería reescribirse como assert code == 201.

Ejecutando las pruebas

El desarrollo del script de prueba está completo, así que procedamos a ejecutar las pruebas. Ejecute el siguiente comando en la línea de comandos para ejecutar el archivo de características:

:: Run a single feature file with the pretty formatter
behave -f pretty features\feature_files_folder\Sample_REST_API_Testing.feature

:: Run every feature in the project
behave

Esto muestra los resultados de la ejecución de la prueba de la siguiente manera:

Ejecutando las pruebas

Visualización de informes en la consola.

La salida desde la consola resulta práctica durante el desarrollo, pero las partes interesadas suelen preferir un informe legible, que es precisamente lo que ofrece Allure.

Informes

Primero, instale el formateador Allure Behave y la herramienta de línea de comandos Allure. El formateador es un Python paquete; la herramienta de línea de comandos es una descarga separada descrita en el Documentación del Informe Allure.

:: 1. Install the formatter
pip install allure-behave

:: 2. Run the tests and write raw results to a folder
behave -f allure_behave.formatter:AllureFormatter -o reports/allure-results features/

:: 3. Render and open the HTML report
allure serve reports/allure-results

Esto genera el informe de resultados de la prueba en un formato presentable e informativo como este:

Informes

Informe de prueba en formato HTML

Informe de prueba en formato HTML

Informe de prueba que muestra el resultado del escenario individual

Un conjunto de herramientas funcional solo es útil si se mantiene legible a medida que crece la API, y es ahí donde la disciplina en los archivos de características da sus frutos.

Mejores prácticas para escribir archivos de características Behave

Una suite Behave se degrada rápidamente cuando los escenarios describen clics y cargas útiles en lugar de comportamiento. Las prácticas que se describen a continuación mantienen los archivos de características legibles para las partes interesadas del negocio, al tiempo que mantienenping <font dir="auto" style="vertical-align: inherit;">las </font> Python Capa mantenible para ingenieros.

  1. Describe el comportamiento, no la implementación. Escriba “Dado que un cliente tiene saldo suficiente”, no “Dado que la columna de saldo es igual a 500”. El archivo de características indica qué hace el sistema; el archivo de pasos indica cómo se verifica.
  2. Mantén un comportamiento por escenario. Un escenario que requiere un código de estado, un cuerpo de respuesta y una fila de base de datos en realidad consta de tres escenarios. Al separarlos, los fallos apuntan directamente a la causa.
  3. Mover la configuración compartida a segundo plano. El ejemplo anterior utiliza Background: Given I set sample REST API url para que cada escenario herede la base URL sin repetirlo.
  4. Parametrizar en lugar de duplicar. Marcadores de posición reforzados como "{request_name}" Dejemos que una función de paso sirva para las aserciones GET, PUT y DELETE, razón por la cual el ejemplo necesita muchas menos definiciones de pasos que escenarios.
  5. Utilice el esquema de escenarios para las variaciones de datos. Cuando se debe demostrar el mismo comportamiento para varias entradas, un Examples: La tabla es más clara que los escenarios copiados.
  6. Evite las dependencias entre escenarios. Behave no garantiza que un escenario POST se ejecute antes que un escenario GET en todas las configuraciones. Cada escenario debe crear el estado que necesita.
  7. Reemplazar las variables globales a nivel de módulo con el contexto. El ejemplo almacena los endpoints y las respuestas en diccionarios de módulos para mayor brevedad. En suites de producción, almacénelos en Behave. context objeto para que el estado se restablezca limpiamente entre escenarios.
  8. Etiquetar escenarios para ejecuciones selectivas. Etiquetas como @smoke or @regression permitir behave --tags=@smoke, lo que mantiene la retroalimentación del proceso rápida.

Aplicar estos hábitos desde el primer archivo de características mantiene la utilidad de un conjunto de BDD mucho más allá de los ejemplos CRUD iniciales. Los equipos que extienden aún más este enfoque a menudo lo combinan con Cucumber para proyectos JVM o Postman para comprobaciones exploratorias de la API.

Preguntas Frecuentes

Ambos leen archivos de características de Gherkin. Cucumber vincula pasos a Java, Rubí o JavaScript, mientras que Behave los vincula a PythonElija la que coincida con su pila de aplicaciones para que las definiciones de pasos reutilicen las utilidades de prueba existentes.

Sí. Behave devuelve un código de salida distinto de cero cuando falla cualquier escenario, por lo que JenkinsGitLab CI o GitHub Actions pueden provocar que la compilación falle automáticamente. Publique la carpeta de resultados de Allure como un artefacto de compilación para generar informes.

Sí. AI Los asistentes elaboran escenarios de tipo Dado-Cuando-Entonces a partir de historias de usuario o de una especificación OpenAPI. RevRevise la salida para detectar casos negativos y reglas de negocio faltantes, ya que los escenarios generados a menudo solo cubren el camino correcto.

Las herramientas de IA detectan escenarios duplicados o casi duplicados, sugieren pasos parametrizados reutilizables y agrupan fallos intermitentes según su causa probable. Esto reduce el esfuerzo de auditoría manual a medida que los archivos de características se multiplican entre los equipos.

Obtén el token una sola vez en el hook before_all de environment.py, guárdalo en el objeto context y adjúntalo como encabezado Authorization en cada paso de la solicitud. Mantén las credenciales en variables de entorno, nunca en archivos de características.

Resumir este post con: