Skip to content

Commit 506d51c

Browse files
authored
Revise README for improved project documentation
Updated README to enhance clarity and detail about the AWS Lambda Testing Framework, including installation instructions, technologies used, and lessons learned.
1 parent be77dd0 commit 506d51c

1 file changed

Lines changed: 282 additions & 60 deletions

File tree

‎README.md‎

Lines changed: 282 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,251 @@
11
# AWS Lambda Testing Framework
22

3-
Framework para testear funciones Lambda en AWS usando Playwright y TypeScript.
3+
> Suite de pruebas para arquitectura serverless en AWS.
4+
> Prueba funciones Lambda, API Gateway, colas SQS y DynamoDB
5+
> con Playwright + TypeScript + AWS SDK v3, con pipeline de
6+
> deploy automático en GitHub Actions.
47
5-
# Para qué sirve
8+
---
69

7-
Básicamente, automaticé las pruebas de toda una arquitectura serverless en AWS. Antes tenía que pedirle al equipo de desarrollo que me ayudara a invocar Lambdas o revisar logs. Ahora lo hago solo.
10+
## Por qué este proyecto existe — y por qué me sorprendió construirlo
811

9-
El proyecto prueba:
10-
- Funciones Lambda
11-
- API Gateway
12-
- Colas SQS
13-
- Base de datos DynamoDB
14-
- Logs en CloudWatch
12+
Cuando aprendí a probar APIs REST, el flujo era simple: mandas una
13+
request, recibes una response, validas el resultado. Todo sincrónico,
14+
todo predecible.
1515

16-
# Cómo funciona
16+
Las arquitecturas serverless funcionan diferente. No hay un servidor
17+
esperando tu request. Una función Lambda se despierta, hace su trabajo,
18+
y el resultado puede llegar segundos después a través de una cola SQS
19+
o quedar guardado en DynamoDB. Probar eso con las mismas técnicas que
20+
usas para un REST tradicional no funciona.
1721

18-
Usuario → API Gateway → SQS → Lambda → OpenWeather API → DynamoDB
22+
Este proyecto nació de una pregunta concreta: ¿cómo prueba un QA
23+
un sistema que no responde de forma síncrona? La respuesta me llevó
24+
a aprender sobre polling en SQS, timeouts configurables por el tipo
25+
de latencia de AWS, y cómo leer logs de CloudWatch desde código
26+
para entender qué pasó dentro de la función.
1927

20-
Si algo falla, va a una cola de errores (DLQ).
28+
---
2129

22-
# Tecnologías que usé
30+
## Arquitectura que se prueba
2331

24-
- Playwright y TypeScript para los tests
25-
- AWS SDK v3 para conectar con AWS
26-
- GitHub Actions para CI/CD
27-
- Node.js 20
32+
```
33+
Usuario
34+
│
35+
▼
36+
API Gateway ──────────────────────────────────────┐
37+
│ │
38+
▼ │
39+
Cola SQS (ColaDeEsperaClima) │
40+
│ │
41+
▼ │
42+
Función Lambda (ClimaLambda) │
43+
│ │
44+
├──► OpenWeather API (clima de la ciudad) │
45+
│ │
46+
├──► DynamoDB (guarda el resultado) │
47+
│ │
48+
└──► Cola SQS (ColaDeResultadoClima) │
49+
│ │
50+
▼ │
51+
Si error ──────────────────────────────────┘
52+
│
53+
▼
54+
DLQ (DLQClima) — cola de mensajes fallidos
55+
```
2856

29-
Servicios AWS:
30-
- Lambda
31-
- API Gateway
32-
- SQS
33-
- DynamoDB
34-
- CloudWatch
35-
- S3
57+
El flujo completo es asíncrono: mandas una ciudad a la API Gateway,
58+
y el resultado del clima llega a otra cola SQS segundos después.
59+
Probar esto requiere estrategias distintas a un test de API tradicional.
3660

37-
# Instalación
38-
bash
61+
---
62+
63+
## Qué se prueba y por qué cada componente importa
64+
65+
### API Gateway — la puerta de entrada
66+
67+
```typescript
68+
// Verificar que la API Gateway acepta la request y la encola
69+
const response = await axios.post(API_GATEWAY_URL, { city: 'Lima' });
70+
expect(response.status).toBe(200);
71+
```
72+
73+
Lo que aprendí aquí: API Gateway tiene dos modos de integración con
74+
Lambda — "Lambda" y "Lambda Proxy". Con el modo "Lambda", el body no
75+
llega como lo mandas. Con "Lambda Proxy", llega tal cual. Tuve que
76+
cambiar la configuración porque mis tests recibían el body como
77+
`undefined` aunque la request era correcta.
78+
79+
### Cola SQS — mensajes que no llegan instantáneo
80+
81+
```typescript
82+
// Polling: esperar hasta que llegue el mensaje con retry
83+
const message = await pollForMessage(SQS_RESULTADO_URL, 30000);
84+
expect(message.city).toBe('Lima');
85+
```
86+
87+
Este fue el desafío más interesante del proyecto. SQS no es un
88+
sistema de respuesta inmediata — los mensajes pueden tardar entre
89+
1 y 10 segundos en estar disponibles. Si haces una sola consulta
90+
y no hay nada, el test falla por error de timing, no por bug real.
91+
92+
Implementé una función de polling con reintentos que consulta la
93+
cola cada 2 segundos hasta que llega el mensaje o se agota el timeout.
94+
Eso hace los tests estables sin depender de `waitForTimeout`.
95+
96+
### Función Lambda — verificar que procesó correctamente
97+
98+
```typescript
99+
// Verificar que Lambda ejecutó sin errores usando CloudWatch
100+
const logs = await getCloudWatchLogs('ClimaLambda', startTime);
101+
expect(logs).not.toContain('ERROR');
102+
expect(logs).toContain('Ciudad procesada: Lima');
103+
```
104+
105+
Antes de este proyecto, "leer logs" significaba abrir la consola
106+
de AWS y buscar manualmente. Aprender a consultar CloudWatch desde
107+
código con el SDK de AWS cambió completamente cómo pienso en la
108+
observabilidad de un sistema — los logs no son solo para debug,
109+
son parte de la verificación.
110+
111+
### DynamoDB — el estado final del sistema
112+
113+
```typescript
114+
// Verificar que el resultado quedó persistido correctamente
115+
const item = await dynamoDb.get({ TableName: 'WeatherResults', Key: { city: 'Lima' } });
116+
expect(item.Item).toBeDefined();
117+
expect(item.Item.temperature).toBeGreaterThan(-50);
118+
```
119+
120+
Validar que el dato llegó a la base de datos cierra el ciclo de
121+
prueba. No basta con que la Lambda ejecute sin errores — el resultado
122+
tiene que haber persistido con los campos correctos.
123+
124+
### DLQ — que los errores se manejen correctamente
125+
126+
```typescript
127+
// Verificar que una ciudad inválida va a la cola de errores
128+
await sendToQueue(SQS_URL, { city: 'CIUDAD_INEXISTENTE_XYZ' });
129+
const dlqMessage = await pollForMessage(SQS_DLQ_URL, 30000);
130+
expect(dlqMessage).toBeDefined();
131+
```
132+
133+
Probar el camino de error es tan importante como probar el camino
134+
exitoso. La DLQ existe para que los mensajes que fallaron no se
135+
pierdan — verificar que funciona es parte de garantizar que el
136+
sistema es confiable.
137+
138+
---
139+
140+
## Problemas reales que resolví
141+
142+
**API Gateway no parseaba el body**
143+
144+
La Lambda recibía `undefined` aunque mandaba el body correctamente.
145+
Causa: la integración estaba configurada como "Lambda" en lugar de
146+
"Lambda Proxy". Con "Lambda", el body llega envuelto en un objeto
147+
de evento y hay que parsearlo manualmente. Cambié a "Lambda Proxy"
148+
y el body llegó directo como JSON.
149+
150+
**Tests fallaban en GitHub Actions pero pasaban en local**
151+
152+
En local, la latencia con AWS era ~100ms. En el runner de GitHub
153+
Actions en us-east-1, era ~800ms. Los tests con timeout de 5 segundos
154+
fallaban por ese margen. Solución: aumentar timeouts a 30 segundos
155+
para operaciones de red con AWS, y usar polling en lugar de esperas
156+
fijas.
157+
158+
**AWS SDK v2 deprecado**
159+
160+
Empecé con la versión 2 del SDK siguiendo tutoriales. A mitad del
161+
proyecto noté que estaba deprecated. Migré a v3, que tiene imports
162+
modulares — en lugar de importar todo el SDK, importas solo el
163+
cliente que necesitas:
164+
165+
```typescript
166+
// v2 — importa todo
167+
import AWS from 'aws-sdk';
168+
169+
// v3 — importa solo lo necesario
170+
import { SQSClient, SendMessageCommand } from '@aws-sdk/client-sqs';
171+
```
172+
173+
Eso redujo el bundle size y hizo los imports más explícitos.
174+
175+
**Mensajes SQS que tardaban en llegar**
176+
177+
El primer intento tenía un solo `receive` sin reintentos. Si el
178+
mensaje tardaba más de lo esperado, el test fallaba. Implementé
179+
una función de polling:
180+
181+
```typescript
182+
async function pollForMessage(queueUrl: string, timeoutMs: number) {
183+
const deadline = Date.now() + timeoutMs;
184+
while (Date.now() < deadline) {
185+
const message = await receiveMessage(queueUrl);
186+
if (message) return message;
187+
await sleep(2000);
188+
}
189+
throw new Error(`Timeout: no llegó mensaje en ${timeoutMs}ms`);
190+
}
191+
```
192+
193+
---
194+
195+
## Los 27 tests y por qué uno es flaky
196+
197+
El proyecto tiene 27 tests en total. 26 pasan consistentemente.
198+
1 es flaky — el test de concurrencia que manda 10 requests simultáneas
199+
y verifica que todas se procesen.
200+
201+
Lo dejé así de forma intencional y lo documenté. En un sistema real,
202+
un test flaky no se elimina ni se ignora — se investiga, se documenta
203+
la causa (en este caso, latencia variable de AWS bajo carga), y se
204+
toma una decisión: aceptar el flakiness con monitoreo, o rediseñar
205+
la arquitectura para que sea más predecible.
206+
207+
Esconder tests flaky marcándolos como skip es una mala práctica
208+
que oculta problemas reales.
209+
210+
---
211+
212+
## Lo que este proyecto me enseñó sobre QA en sistemas distribuidos
213+
214+
Probar un sistema serverless es fundamentalmente diferente a probar
215+
una API monolítica. Las principales diferencias que encontré:
216+
217+
**El tiempo es parte del contrato.** En REST pruebas que el status
218+
sea 200. En sistemas asíncronos también pruebas que el resultado
219+
llegue dentro de un tiempo razonable. Un sistema que siempre da el
220+
resultado correcto pero tarda 5 minutos está roto desde la perspectiva
221+
del usuario.
222+
223+
**Los logs son parte de las pruebas.** En sistemas distribuidos,
224+
verificar que el proceso corrió correctamente a veces solo es posible
225+
leyendo los logs. CloudWatch no es solo para debug — es evidencia
226+
de lo que ocurrió dentro de la función.
227+
228+
**Los errores tienen que ir a algún lado.** La DLQ no es un detalle
229+
de implementación — es parte del diseño de calidad. Un mensaje que
230+
falla y desaparece sin dejar rastro es peor que uno que falla y
231+
queda en la DLQ para ser analizado.
232+
233+
---
234+
235+
## Cómo ejecutar
236+
237+
**Requisitos:** Node.js 20+, cuenta AWS con permisos en Lambda, SQS, DynamoDB y CloudWatch
238+
239+
```bash
39240
git clone https://github.com/ipanaque94/aws-lambda-testing.git
40241
cd aws-lambda-testing
41242
npm install
42243
npx playwright install chromium
244+
```
43245

246+
Crea un archivo `.env` basado en `.env.example`:
44247

45-
# Configuración
46-
47-
Crear archivo .env:
48-
248+
```env
49249
AWS_REGION=us-east-1
50250
AWS_ACCESS_KEY_ID=tu_access_key
51251
AWS_SECRET_ACCESS_KEY=tu_secret_key
@@ -54,50 +254,72 @@ SQS_RESULTADO_URL=https://sqs.us-east-1.amazonaws.com/CUENTA/ColaDeResultadoClim
54254
SQS_DLQ_URL=https://sqs.us-east-1.amazonaws.com/CUENTA/DLQClima
55255
OPENWEATHER_API_KEY=tu_api_key
56256
API_GATEWAY_URL=https://xxx.execute-api.us-east-1.amazonaws.com/prod
257+
```
57258

58-
59-
# Ejecutar tests
60-
bash
259+
```bash
260+
# Ejecutar todos los tests
61261
npm test
262+
263+
# Ver reporte HTML
62264
npx playwright show-report
265+
```
266+
267+
---
268+
269+
## Pipeline CI/CD
270+
271+
El workflow de GitHub Actions hace dos cosas en secuencia:
272+
273+
1. Corre todos los tests contra el ambiente de AWS
274+
2. Si los tests pasan, despliega las funciones Lambda automáticamente
63275

64-
# Lo que aprendí
276+
```
277+
Push a main
278+
│
279+
▼
280+
Tests automatizados (27 tests)
281+
│
282+
▼ (solo si pasan)
283+
Deploy de Lambda Functions
284+
│
285+
▼
286+
Reporte guardado 30 días como artefacto
287+
```
65288

66-
- Invocar Lambdas sin usar la consola de AWS
67-
- Leer logs de CloudWatch en tiempo real
68-
- Validar mensajes en colas SQS
69-
- Trabajar con DynamoDB
70-
- Configurar Lambda Proxy en API Gateway
71-
- Hacer deployment automático con GitHub Actions
289+
Los secrets de AWS se configuran en GitHub y nunca se exponen en el código.
72290

73-
# Problemas que tuve
291+
---
74292

75-
API Gateway no parseaba el body: Tuve que cambiar la integración a Lambda Proxy.
293+
## Stack
76294

77-
Tests fallaban en GitHub Actions pero pasaban local: Aumenté timeouts porque la latencia de red es mayor en CI/CD.
295+
| Herramienta | Uso |
296+
|---|---|
297+
| Playwright + TypeScript | Framework de tests y tipado estático |
298+
| AWS SDK v3 | Cliente para Lambda, SQS, DynamoDB, CloudWatch |
299+
| Node.js 20 | Runtime |
300+
| GitHub Actions | CI/CD con deploy automático post-tests |
78301

79-
AWS SDK deprecado: Migré de SDK v2 a v3.
302+
**Servicios AWS:**
303+
Lambda · API Gateway · SQS · DynamoDB · CloudWatch · S3
80304

81-
Mensajes SQS tardaban en llegar: Implementé reintentos con polling.
305+
---
82306

83-
# Resultados
307+
## Dónde encaja en mi aprendizaje
84308

85-
27 tests en total
86-
- 26 pasan siempre
87-
- 1 es flaky (test de concurrencia por latencia de AWS)
88-
- Tiempo: 1-2 minutos
309+
Este proyecto fue una extensión natural del trabajo con APIs REST.
310+
Después de aprender a probar endpoints síncronos con
311+
[RestAssured](https://github.com/ipanaque94/RestAssured3APIS) y
312+
[Playwright](https://github.com/ipanaque94/playwright-professional-framework),
313+
quería entender cómo cambia el testing cuando el sistema es asíncrono
314+
y distribuido — que es la realidad de muchas arquitecturas modernas
315+
en la nube.
89316

90-
# GitHub Actions
317+
---
91318

92-
Cada push ejecuta:
93-
1. Tests automatizados
94-
2. Si pasan, despliega las Lambdas
95-
3. Guarda reporte 30 días
319+
## Autor
96320

97-
Necesitas configurar secrets en GitHub con tus credenciales AWS.
321+
**Enoc Ipanaque** — Lima, Perú
98322

99-
# Autor
323+
QA Automation Engineer en formación, estudiando para ISTQB Foundation Level.
100324

101-
Enoc Panaque
102-
- GitHub: @ipanaque94
103-
- LinkedIn: linkedin.com/in/enoc-panaque
325+
[LinkedIn](www.linkedin.com/in/enoc-isaac-ipanaque-rodas-b3729a283) · [GitHub](https://github.com/ipanaque94)

0 commit comments

Comments
 (0)