Antipatrones Gherkin
Antipatrones Gherkin
Escribir Gherkin sintácticamente correcto es fácil. Escribir Gherkin que sirva como living documentation y test mantenible requiere disciplina. Esta sección lista los antipatrones más frecuentes.
1. Imperativo en lugar de declarativo
Imperativo (mal) — describe pasos mecánicos de UI:
Scenario: Login del usuario
Given el usuario abre el navegador
And navega a "https://app.example.com/login"
And espera 2 segundos
When ingresa "[email protected]" en el campo con id "email"
And ingresa "secret123" en el campo con id "password"
And hace clic en el botón con id "submit"
And espera 1 segundo
Then la URL es "https://app.example.com/dashboard"
And el elemento con id "user-greeting" contiene "Hola Ana"
Declarativo (bien) — describe qué hace el usuario, no cómo:
Scenario: Usuario obtiene acceso al dashboard con credenciales válidas
Given un usuario registrado con email "[email protected]"
When inicia sesión con email "[email protected]" y password "secret123"
Then ve el dashboard
And el dashboard saluda a "Ana"
Por qué: el imperativo se rompe con cada cambio de UI (id renombrado, espera distinta, página redirige diferente). El declarativo solo se rompe si el comportamiento cambia.
Las step definitions absorben los detalles técnicos:
When('inicia sesión con email {string} y password {string}', async function (email, password) {
await this.page.goto('/login')
await this.page.fill('#email', email)
await this.page.fill('#password', password)
await this.page.click('#submit')
await this.page.waitForURL(/.*\/dashboard/)
})
2. UI-coupled
Mal:
When hago clic en el cuarto link del segundo menú de la sidebar
Bien:
When abro la sección de configuración de notificaciones
Si reordenan los links o renombran “sidebar” → “rail”, el feature sigue funcionando. La step definition cambia.
3. Scenarios dependientes
Mal:
Scenario: Crear usuario
When creo un usuario "[email protected]"
Then existe el usuario "[email protected]"
Scenario: Login con el usuario recién creado
When envío POST /auth/login con email "[email protected]" ⚠ asume estado del scenario anterior
Then la respuesta tiene status 200
Bien:
Scenario: Crear usuario
When creo un usuario "[email protected]"
Then existe el usuario "[email protected]"
Scenario: Login con usuario existente
Given un usuario registrado con email "[email protected]" y password "secret123"
When envío POST /auth/login con email "[email protected]" y password "secret123"
Then la respuesta tiene status 200
Cada scenario debe ser independiente. Si necesitás el setup, declarálo explícitamente.
4. Scenarios gigantes
Mal: 30 steps en un scenario.
Scenario: Flujo completo de e-commerce
Given un usuario registrado
When ...
And ...
And ...
And ...
And ...
(25 steps más)
Then ...
Bien: partir en scenarios enfocados.
Scenario: Agregar productos al carrito
...
Scenario: Aplicar cupón de descuento
...
Scenario: Completar checkout
...
Scenario: Recibir email de confirmación
...
Regla: 3-10 steps por scenario es ideal. Más de 15, partilo.
5. Detalles técnicos en el feature
Mal:
Scenario: Cargar productos
Given la tabla "products" tiene la fila (id=100, sku=P-100, price=25)
When envío SELECT * FROM products WHERE id=100
Then la respuesta JSON tiene { "id": 100, "sku": "P-100", "price": 25.00 }
Bien:
Scenario: Consultar un producto del catálogo
Given el producto "P-100" en el catálogo con precio 25
When solicito el producto "P-100"
Then recibo el producto con sku "P-100" y precio 25
Las step definitions encapsulan SQL, JSON y otros detalles. El feature habla en lenguaje de dominio.
6. Aserciones en Given o When
Mal:
Given el carrito tiene 3 productos ⚠ ¿es precondición o aserción?
When el carrito tiene 3 productos ⚠ esto no es una acción
Bien:
Givenestablece contexto (verbo declarativo: “tiene”, “está”)Whenejecuta acción (verbo de acción: “agrega”, “envía”, “hace clic”)Thenverifica resultado (verbo de verificación: “tiene”, “es”, “contiene”)
Given un carrito con 3 productos
When agrego un producto adicional
Then el carrito tiene 4 productos
7. Tres “Then” sin claridad
Mal:
Then el sistema responde
And se guarda en la base
And se envía un email
Mejor:
Then la respuesta tiene status 201
And la base de datos tiene una nueva orden con status "pending"
And se envía un email de confirmación a "[email protected]"
Las aserciones deben ser específicas y verificables.
8. Negaciones vagas
Mal:
Then el sistema no falla
And no muestra errores
Bien:
Then la respuesta tiene status 200
And el cuerpo no contiene el campo "error"
And el log no contiene entradas con nivel "ERROR"
“No falla” no es verificable. Cuantificá la negación.
9. Lógica condicional en pasos
Mal:
Given un usuario
When envía credenciales
Then si el usuario es admin ve el panel admin
And si es regular ve el dashboard
Bien: partir en dos scenarios.
Scenario: Admin ve el panel admin
Given un usuario administrador
When inicia sesión
Then ve el panel admin
Scenario: Usuario regular ve el dashboard
Given un usuario regular
When inicia sesión
Then ve el dashboard
10. Reutilizar steps con significado distinto
Mal:
// step definitions
Given('un usuario "{email}"', function (email) {
this.user = createUser(email) // a veces crea, a veces busca
})
Si distinto código en cada llamada, separá:
Given('un usuario nuevo con email {string}', function (email) {
this.user = this.api.createUser(email)
})
Given('un usuario existente con email {string}', function (email) {
this.user = this.db.findUser({ email })
})
El feature queda explícito.
11. Tiempos y delays mágicos
Mal:
When hago clic en "Guardar"
And espero 5 segundos
Then la lista contiene "Nuevo registro"
Bien: la step definition espera explícitamente lo que necesita, no un timeout fijo.
When('hago clic en {string}', async function (label) {
await this.page.click(`button:has-text("${label}")`)
})
Then('la lista contiene {string}', async function (text) {
await this.page.waitForSelector(`text="${text}"`, { timeout: 10000 })
})
El waitFor espera el resultado real. No usar sleep(5000).
12. Steps que ocultan complejidad
Mal:
When ejecuto la magia
Bien:
When proceso la orden con pago tarjeta de crédito y envío estándar
Si el step tiene un nombre vago, los lectores no entienden qué pasa. Nombrá explícitamente.
13. Tags inventados ad-hoc
Mal:
@check @temporary @do-not-skip @level-3 @important
Scenario: ...
Bien: vocabulario fijo documentado.
@auth @critical @REQ-AUTH-001 @smoke
Scenario: ...
14. Background como contenedor de basura
Mal:
Background:
Given un usuario
And configurar mocks
And seed de DB
And feature flag X enabled
And feature flag Y disabled
And mock del servicio de email
And mock del servicio de pagos
And el reloj fijado en 2024-01-15
And ...
Bien: pasar setup técnico a Before hooks. Mantener Background solo con setup de dominio.
Background:
Given un usuario registrado con plan "pro"
15. Feature sin descripción
Mal:
Feature: Login
Scenario: ...
Bien:
Feature: Autenticación de usuarios
Como sistema multiusuario, debemos permitir a los
usuarios autenticarse antes de acceder a datos
personales. Esta feature cubre login, logout y
manejo de sesión.
Scenario: ...
La descripción explica el por qué.
16. Scenarios sin propósito claro
Mal:
Scenario: Test 1
Scenario: Test 2
Bien:
Scenario: Usuario obtiene JWT con credenciales válidas
Scenario: Login con email inexistente devuelve 401
El título del scenario describe el comportamiento que verifica.
17. Asumir order de Examples
Mal:
Scenario Outline:
When agrego "<sku>"
Then el total es <total>
Examples:
| sku | total |
| P-100 | 25 |
| P-101 | 105 | # ⚠ asume que P-100 se sumó del scenario anterior
Cada fila del Outline es un scenario independiente. El segundo no acumula del primero.
18. Mockear demasiado
Mal:
Given el servicio de pagos retorna "success"
And el servicio de email retorna "delivered"
And el servicio de DB retorna 1 row
When proceso la orden
Then todo OK
Si mockeás todo, no estás testeando el sistema — estás testeando los mocks.
Mejor: usar contract tests para servicios externos, y BDD para el sistema completo o módulos de dominio. Mockear solo el límite externo.
Checklist anti-antipatrones
Antes de aprobar un feature:
- Está en lenguaje declarativo (no imperativo)
- No tiene detalles de UI (ids, clases, paths)
- Scenarios independientes: no asumen estado de otros
- Scenarios de 3-10 steps
- Detalles técnicos en step definitions, no en
.feature - Aserciones específicas y verificables
- Sin lógica condicional en pasos
- Sin sleeps mágicos
- Background mínimo (3-5 steps de dominio)
- Tags del vocabulario documentado
- Descripción de feature explica el por qué
- Títulos de scenario describen comportamiento
En el siguiente capítulo vemos cómo aprovechar Gherkin como living documentation.