Scenario Outline y Examples

Por: Artiko
gherkinscenario-outlineexamplesparametrizacion

Scenario Outline y Examples

Scenario Outline es una plantilla parametrizada: el mismo flujo con distintos valores de entrada y salida. Reemplaza N scenarios casi idénticos por una sola plantilla y una tabla de ejemplos.

Sintaxis básica

Scenario Outline: Validación de password durante registro
  Given un formulario de registro abierto
  When el usuario escribe "<password>" en el campo password
  Then el indicador de fuerza muestra "<strength>"
  And el botón "Crear cuenta" está "<enabled_or_disabled>"

  Examples:
    | password         | strength | enabled_or_disabled |
    | abc              | weak     | disabled            |
    | abcdef123        | medium   | disabled            |
    | Abc!def#123$xyz  | strong   | enabled             |

Cuándo usar Outline

✅ El mismo flujo con varios inputs:

❌ Cuándo NO usarlo:

Ejemplo: límites de plan

Scenario Outline: Límite de export según subscripción
  Given un usuario con subscripción "<plan>"
  When solicita un export de "<size_mb>" MB
  Then la respuesta tiene status <expected_status>
  And el cuerpo contiene "<expected_message>"

  Examples:
    | plan    | size_mb | expected_status | expected_message              |
    | free    | 10      | 200             | Export started                |
    | free    | 50      | 413             | Upgrade to Pro for larger exports |
    | pro     | 50      | 200             | Export started                |
    | pro     | 200     | 413             | Upgrade to Premium            |
    | premium | 500     | 200             | Export started                |
    | premium | 2000    | 413             | Maximum export size exceeded  |

Una sola plantilla, seis casos cubiertos, lectura clara.

Múltiples tablas Examples

Podés tener varias tablas, cada una con un nombre opcional, para agrupar casos por categoría:

Scenario Outline: Validación de email
  Given el formulario de registro abierto
  When escribo "<email>" en el campo email
  Then la validación es "<result>"

  Examples: Emails válidos
    | email                | result |
    | [email protected]      | valid  |
    | [email protected] | valid  |
    | [email protected] | valid  |

  Examples: Emails inválidos
    | email           | result   |
    |                 | invalid  |
    | not-an-email    | invalid  |
    | @example.com    | invalid  |
    | user@           | invalid  |
    | [email protected]       | invalid  |

Los reportes agrupan los scenarios por nombre del Examples.

Outline y tags

Los tags aplican a todos los scenarios generados:

@validation @REQ-AUTH-005
Scenario Outline: Validación de password
  ...

  @critical
  Examples: Casos críticos
    | password | strength |
    | abc      | weak     |
    | a        | weak     |

  @edge-cases
  Examples: Bordes
    | password                                                            | strength |
    | exactly-twelve-chars                                                | medium   |
    | Verylongpassword!@#$%^&*()_+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa  | strong   |

Cómo se ven en step definitions

Las step definitions no cambian: usás los placeholders en el patrón.

TypeScript:

import { Given, When, Then, DataTable } from '@cucumber/cucumber'

When('escribo {string} en el campo email', function (email: string) {
  this.formData.email = email
})

Then('la validación es {string}', function (result: string) {
  if (result === 'valid') {
    assert.equal(this.validator.validate(this.formData.email), true)
  } else {
    assert.equal(this.validator.validate(this.formData.email), false)
  }
})

Python (behave):

from behave import when, then

@when('escribo "{email}" en el campo email')
def step_type_email(context, email):
    context.form_data['email'] = email

@then('la validación es "{result}"')
def step_validation(context, result):
    is_valid = context.validator.validate(context.form_data['email'])
    if result == 'valid':
        assert is_valid, f"esperado válido, fue inválido: {context.form_data['email']}"
    else:
        assert not is_valid, f"esperado inválido, fue válido: {context.form_data['email']}"

Antipatrón 1: Outline con flujos distintos

Scenario Outline: Login
  Given un usuario "<type>"
  When envía credenciales
- And navega a "<page>"                # ⚠ solo aplica si es admin
  Then ve "<expected>"

  Examples:
    | type  | page    | expected         |
    | user  |         | dashboard        |
    | admin | /admin  | panel admin      |

Si el flujo cambia según el caso, no es un Outline — son scenarios distintos:

+ Scenario: Login como usuario regular
+   ...
+
+ Scenario: Login como administrador
+   ...

Antipatrón 2: tablas gigantes

Scenario Outline: Crear factura
  Given ...
  When crear factura con datos
  Then ...

  Examples:
- | a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p |
- | ...                                                            |

Si necesitás 15 columnas, partí en varios Outlines más enfocados.

Antipatrón 3: encadenar Outlines

- Scenario Outline: Crear usuario
-   ...
- 
- Scenario Outline: Editar usuario  ⚠ usa los usuarios del Outline anterior
-   Given un usuario creado en el scenario anterior

Cada scenario (incluidos los generados por Outline) debe ser independiente. No asumas estado de otros scenarios.

Anti-patrón 4: Lógica condicional en pasos

Scenario Outline: Comportamiento por plan
  ...
  Then la respuesta es "<expected>"
- And si el plan es "free" muestra el upsell  ⚠ lógica condicional en step

Mejor partir en dos Outlines o usar Where (en lenguaje EARS, ver tutorial EARS).

Diagrama: expansión de un Outline

flowchart LR
    Outline[Scenario Outline] --> R1[Row 1]
    Outline --> R2[Row 2]
    Outline --> R3[Row 3]
    R1 --> S1[Scenario instanciado 1]
    R2 --> S2[Scenario instanciado 2]
    R3 --> S3[Scenario instanciado 3]

Cada fila → un scenario. Cada scenario ejecuta independiente.

Ejemplo completo: validación de carrito

Feature: Validación de límites del carrito

  Background:
    Given un cliente registrado

  Scenario Outline: Límites de quantity por producto
    When el cliente agrega "<sku>" con quantity <qty>
    Then la respuesta tiene status <status>
    And el cuerpo de error es "<error_message>"

    Examples: Quantities válidas
      | sku   | qty | status | error_message |
      | P-100 | 1   | 200    |               |
      | P-100 | 10  | 200    |               |
      | P-100 | 100 | 200    |               |

    Examples: Quantities inválidas
      | sku   | qty  | status | error_message              |
      | P-100 | 0    | 400    | quantity must be at least 1 |
      | P-100 | -1   | 400    | quantity must be at least 1 |
      | P-100 | 101  | 400    | quantity cannot exceed 100 |
      | P-100 | 9999 | 400    | quantity cannot exceed 100 |

Una sola plantilla cubre el happy path y siete casos de error con bordes explícitos.

Outline + Background + Tags = combo poderoso

@cart @validation
Feature: Validación de carrito

  Background:
    Given un cliente registrado

  @happy-path @REQ-CART-001
  Scenario Outline: Agregar productos al carrito
    When el cliente agrega "<sku>" con quantity <qty>
    Then el carrito contiene la línea (<sku>, <qty>)

    Examples:
      | sku   | qty |
      | P-100 | 1   |
      | P-101 | 5   |

  @error-cases @REQ-CART-002
  Scenario Outline: Quantities inválidas son rechazadas
    When el cliente agrega "<sku>" con quantity <qty>
    Then la respuesta tiene status 400
    And el mensaje contiene "<error_message>"

    Examples:
      | sku   | qty  | error_message              |
      | P-100 | 0    | quantity must be at least 1 |
      | P-100 | 101  | quantity cannot exceed 100 |

Combinás Background (precondición común), Tags (organización) y Scenario Outline (parametrización). Es la forma canónica de escribir features mantenibles.

Resumen

En el siguiente capítulo cubrimos tags y ejecución selectiva.