Serverless Framework In-Depth
Serverless.yml Configuration
The core configuration file serverless.yml in Serverless Framework is key to defining the entire service architecture. This YAML file includes service metadata, resource definitions, function configurations, and various trigger settings.
Basic Configuration Structure
A typical serverless.yml file includes the following main sections:
service: my-serverless-service # Service name
provider:
name: aws # Cloud provider
runtime: nodejs14.x # Runtime environment
region: us-east-1 # Default region
stage: dev # Default stage
profile: default # AWS CLI profile
environment:
VARIABLE1: value1 # Environment variables
VARIABLE2: value2
iamRoleStatements: # IAM permission policies
- Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:PutItem
Resource: "arn:aws:dynamodb:us-east-1:123456789012:table/my-table"
functions:
helloWorld:
handler: handler.helloWorld # Function handler path
events: # Trigger configuration
- http:
path: hello
method: get
cors: true
plugins:
- serverless-offline # Plugin list
- serverless-dotenv-plugin
Advanced Configuration Options
Custom Domain Configuration:
custom:
customDomain:
domainName: api.myapp.com
basePath: ''
stage: ${self:provider.stage}
createRoute53Record: true
Resource Definition:
resources:
Resources:
MyDynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: MyTable
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
Stage Configuration Overrides:
provider:
name: aws
runtime: nodejs14.x
stage: ${opt:stage, 'dev'} # Get stage from CLI, default to dev
functions:
helloWorld:
handler: handler.helloWorld
environment:
STAGE: ${self:provider.stage} # Use current stage value
Multi-Environment Deployment
In real-world projects, deployment across different environments (development, testing, production) is common. Serverless Framework offers multiple ways to manage multi-environment configurations.
Environment Variable Management
Method 1: Define Directly in serverless.yml:
provider:
environment:
NODE_ENV: ${opt:stage, self:custom.defaultStage}
DB_HOST: ${env:DB_HOST, 'localhost'}
Method 2: Use .env Files:
Create .env.dev, .env.prod, etc.:
# .env.dev
DB_HOST=localhost
DB_PORT=5432
# .env.prod
DB_HOST=db.example.com
DB_PORT=5432
Reference in serverless.yml:
plugins:
- serverless-dotenv-plugin
custom:
dotenv:
include:
- DB_HOST
- DB_PORT
Method 3: Use serverless-pseudo-parameters Plugin:
Install plugin:
npm install --save-dev serverless-pseudo-parameters
Configure usage:
provider:
environment:
TABLE_NAME: !Ref MyDynamoDBTable
BUCKET_NAME: !Sub "my-bucket-${opt:stage, self:provider.stage}"
Environment-Specific Deployment Commands
# Development environment deployment
serverless deploy --stage dev
# Testing environment deployment
serverless deploy --stage test
# Production environment deployment
serverless deploy --stage prod
Conditional Resource Creation
resources:
Resources:
DevOnlyResource:
Type: AWS::S3::Bucket
Condition: IsDevStage
ProdOnlyResource:
Type: AWS::S3::Bucket
Condition: IsProdStage
conditions:
IsDevStage: !Equals [!Ref "AWS::StackName", "dev"]
IsProdStage: !Equals [!Ref "AWS::StackName", "prod"]
Note: The above condition example is incorrect. A correct approach would be:
custom:
isDev: ${opt:stage, 'dev'} == 'dev'
isProd: ${opt:stage, 'dev'} == 'prod'
resources:
Resources:
DevOnlyResource:
Type: AWS::S3::Bucket
Condition: IsDev
ProdOnlyResource:
Type: AWS::S3::Bucket
Condition: IsProd
conditions:
IsDev: !Equals ["${self:custom.isDev}", "true"]
IsProd: !Equals ["${self:custom.isProd}", "true"]
A more practical approach is:
- Use separate configuration files
- Use plugins like
serverless-plugin-ifelse - Handle conditional logic in deployment scripts
Using serverless-plugin-ifelse for Conditional Deployment
Install plugin:
npm install --save-dev serverless-plugin-ifelse
Configure:
plugins:
- serverless-plugin-ifelse
custom:
ifelse:
- If: '${opt:stage} == "dev"'
Set:
provider.environment.DEBUG: 'true'
resources.Resources.DevOnlyResource.Type: 'AWS::S3::Bucket'
- If: '${opt:stage} == "prod"'
Set:
provider.environment.DEBUG: 'false'
resources.Resources.ProdOnlyResource.Type: 'AWS::S3::Bucket'
Note: The above configuration syntax may be incorrect; refer to the plugin’s documentation for accurate usage.
A more reliable method is to use separate serverless.yml files or handle logic in deployment scripts.
Plugins and Extensions
Serverless Framework’s strength lies in its rich plugin ecosystem, which extends functionality, integrates third-party services, or adds custom behaviors.
Common Plugin Categories
- Deployment Optimization:
serverless-webpack: Bundle functions with Webpackserverless-bundle: Modern bundling solutionserverless-offline: Simulate API Gateway locally
- Environment Management:
serverless-dotenv-plugin: Load .env filesserverless-pseudo-parameters: Use AWS pseudo-parameters
- Monitoring and Logging:
serverless-plugin-log-retention: Set CloudWatch log retention policiesserverless-plugin-canary-deployments: Canary deployments
- Security:
serverless-patch-plugin: Modify generated CloudFormation templatesserverless-iam-roles-per-function: Create per-function IAM roles
- Custom:
serverless-plugin-ifelse: Conditional logicserverless-scriptable-plugin: Run custom scripts
Plugin Installation and Configuration Examples
serverless-webpack Plugin:
Install:
npm install --save-dev serverless-webpack webpack webpack-node-externals
Configure serverless.yml:
plugins:
- serverless-webpack
custom:
webpack:
includeModules:
forceExclude:
- aws-sdk
packager: 'yarn'
serverless-offline Plugin:
Install:
npm install --save-dev serverless-offline
Configure serverless.yml:
plugins:
- serverless-offline
custom:
serverless-offline:
port: 3000
host: 0.0.0.0
httpPort: 4000
httpsProtocol: 'dev-certs'
serverless-bundle Plugin:
Install:
npm install --save-dev serverless-bundle
Configure serverless.yml:
plugins:
- serverless-bundle
custom:
bundle:
linting: false
sourcemaps: true
externalModules:
- aws-sdk
Custom Plugin Development
Steps to create a custom plugin:
- Create plugin directory structure:
my-plugin/
├── index.js
└── package.json
- Write plugin code (
index.js):
'use strict';
class MyPlugin {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
this.hooks = {
'before:deploy:deploy': this.beforeDeploy.bind(this),
};
}
async beforeDeploy() {
this.serverless.cli.log('Running custom pre-deploy logic...');
// Add custom logic
}
}
module.exports = MyPlugin;
- Use in
serverless.yml:
plugins:
- ./my-plugin
Plugin Chain and Execution Order
Plugins execute in the order listed in the plugins array. Hooks control execution timing:
plugins:
- plugin-a
- plugin-b
Example hooks plugins can define:
before:deploy:deployafter:deploy:deploybefore:invoke:invokeafter:invoke:invoke
Multi-Stage Deployment Strategies
For different environments (development, testing, production), Serverless Framework provides multiple configuration options.
Environment Variable Management Best Practices
- Layered Environment Variables:
- Global variables (shared across stages)
- Stage-specific variables
- Local development variables
- Secure Storage:
- Use AWS Secrets Manager for sensitive data
- Store non-sensitive data in .env files or directly in serverless.yml
- Dynamic Loading:
- Load configurations dynamically based on deployment stage
Deployment Workflow Example
- Development Environment:
serverless deploy --stage dev --aws-profile dev-account
- Testing Environment:
serverless deploy --stage test --aws-profile test-account
- Production Environment:
serverless deploy --stage prod --aws-profile prod-account
Conditional Resource Creation Example
resources:
Resources:
DevOnlyResource:
Type: AWS::S3::Bucket
Condition: CreateDevResources
ProdOnlyResource:
Type: AWS::S3::Bucket
Condition: CreateProdResources
conditions:
CreateDevResources: !Equals ["${opt:stage, 'dev'}", "dev"]
CreateProdResources: !Equals ["${opt:stage, 'dev'}", "prod"]
Multi-Account Deployment
Configure multiple AWS accounts:
# ~/.aws/credentials
[dev-account]
aws_access_key_id = DEV_ACCESS_KEY
aws_secret_access_key = DEV_SECRET_KEY
[test-account]
aws_access_key_id = TEST_ACCESS_KEY
aws_secret_access_key = TEST_SECRET_KEY
[prod-account]
aws_access_key_id = PROD_ACCESS_KEY
aws_secret_access_key = PROD_SECRET_KEYDeployment commands:
# Development environment
serverless deploy --stage dev --aws-profile dev-account
# Testing environment
serverless deploy --stage test --aws-profile test-account
# Production environment
serverless deploy --stage prod --aws-profile prod-account
Blue-Green Deployment Strategy
Implement blue-green deployments with AWS CodeDeploy:
- Configure CodeDeploy:
provider:
name: aws
deploymentBucket:
name: my-deployment-bucket
deployment:
type: CodeDeploy
deploymentGroupName: my-deployment-group
- Deploy:
serverless deploy --stage prod
- Switch traffic manually or automatically:
- Use ALB/ELB to switch target groups
- Use Route53 weighted routing
Cloud-Native Tool Integration
AWS SAM (Serverless Application Model)
AWS SAM is AWS’s official Serverless Application Model, extending CloudFormation template syntax to simplify Serverless application definition and deployment.
SAM Basic Configuration
SAM Template Structure:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-world/
Handler: app.lambdaHandler
Runtime: nodejs14.x
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
Outputs:
HelloWorldApi:
Description: "API Gateway endpoint URL for Prod stage"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
SAM CLI Usage
- Install SAM CLI:
# macOS
brew tap aws/tap
brew install aws-sam-cli
# Windows
choco install aws-sam-cli
# Linux
curl -Lo sam-installation.zip https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip
unzip sam-installation.zip -d sam-installation
sudo ./sam-installation/install
- Local Testing:
sam build
sam local start-api
- Deployment:
sam deploy --guided
SAM vs. Serverless Framework Comparison
| Feature | AWS SAM | Serverless Framework |
|---|---|---|
| Definition Method | CloudFormation extension | Custom YAML format |
| Plugin Ecosystem | Limited | Rich |
| Multi-Cloud Support | AWS only | Multi-cloud support |
| Learning Curve | Steeper | Gentler |
| Advanced Features | Limited | Rich |
| Community Support | Official support | Community-driven |
Hybrid Usage Example
Integrate SAM resources in a Serverless Framework project:
- Create
sam-resources.yaml:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
MySAMFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: sam-function/
Handler: app.lambdaHandler
Runtime: nodejs14.x
- Reference in
serverless.yml:
resources:
- ${file(sam-resources.yaml)}
Note: This method may not be fully compatible and requires testing.
Azure Functions Core Tools
Azure Functions Core Tools is a CLI for developing and managing Azure Functions.
Installation and Configuration
- Installation:
# Windows
npm install -g azure-functions-core-tools@4 --unsafe-perm true
# macOS/Linux
brew tap azure/functions
brew install azure-functions-core-tools@4
# or
npm install -g azure-functions-core-tools@4 --unsafe-perm true
- Local Run:
func start
- Create Function:
func init MyFunctionProj --typescript
cd MyFunctionProj
func new --name HttpExample --template "HTTP trigger"
Configuration Files
host.json example:
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[2.*, 3.0.0)"
}
}
local.settings.json example:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "node",
"MY_SETTING": "value"
}
}
Deploy to Azure
- Login:
az login
- Deploy:
func azure functionapp publish <FunctionAppName>
Google Cloud SDK
Google Cloud SDK is a CLI for managing Google Cloud resources.
Installation and Configuration
- Installation:
# Linux
curl https://sdk.cloud.google.com | bash
exec -l $SHELL
gcloud init
# macOS
brew install --cask google-cloud-sdk
gcloud init
# Windows
choco install google-cloud-sdk
gcloud init
- Configure Project:
gcloud config set project [PROJECT_ID]
Deploy Cloud Functions
- Create Function:
gcloud functions deploy helloWorld \
--runtime nodejs14 \
--trigger-http \
--allow-unauthenticated
- View Function:
gcloud functions describe helloWorld
- Invoke Function:
gcloud functions call helloWorld --data '{"name":"World"}'
Configuration File
cloudbuild.yaml example:
steps:
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
args: ['gcloud', 'functions', 'deploy', 'helloWorld',
'--runtime', 'nodejs14',
'--trigger-http',
'--allow-unauthenticated']
Local Development and Testing
LocalStack and Service Simulation
LocalStack is a fully managed local AWS service implementation for development and testing.
Installation and Configuration
- Install Docker:
# macOS
brew install docker
# Ubuntu
sudo apt-get update
sudo apt-get install docker.io
- Run LocalStack:
docker run --rm -it -p 4566:4566 -p 4571:4571 localstack/localstack
- Configure AWS CLI:
aws --endpoint-url=http://localhost:4566 configure
Using LocalStack to Simulate Services
- S3 Simulation:
aws --endpoint-url=http://localhost:4566 s3 mb s3://my-bucket
aws --endpoint-url=http://localhost:4566 s3 ls
- Lambda Simulation:
# Create Lambda function
aws --endpoint-url=http://localhost:4566 lambda create-function \
--function-name my-function \
--runtime python3.8 \
--role arn:aws:iam::000000000000:role/lambda-execution-role \
--handler lambda_function.handler \
--zip-file fileb://function.zip
# Invoke Lambda function
aws --endpoint-url=http://localhost:4566 lambda invoke \
--function-name my-function \
output.json
Serverless Offline
The Serverless Offline plugin allows local simulation of API Gateway and Lambda functions.
Installation and Configuration
- Install Plugin:
npm install --save-dev serverless-offline
- Configure serverless.yml:
plugins:
- serverless-offline
custom:
serverless-offline:
port: 3000
host: 0.0.0.0
httpPort: 4000
httpsProtocol: 'dev-certs'
- Run Local Service:
serverless offline start
Advanced Features
- Simulate Lambda Events:
serverless invoke local --function helloWorld --path event.json
- Simulate Multiple Lambdas:
functions:
func1:
handler: handler.func1
events:
- http:
path: func1
method: get
func2:
handler: handler.func2
events:
- http:
path: func2
method: post
- Simulate DynamoDB:
custom:
serverless-offline:
dynamodb:
start:
port: 8000
inMemory: true
migrate: true
Testing Tools and Mocks
Unit Testing
- Jest Configuration:
npm install --save-dev jest @types/jest ts-jest
- jest.config.js:
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
coverageDirectory: 'coverage',
collectCoverageFrom: ['src/**/*.{js,ts}'],
};
- Test Example:
// handler.test.ts
import { handler } from '../src/handler';
describe('Handler', () => {
it('should return hello world', async () => {
const event = { key: 'value' };
const result = await handler(event);
expect(result).toEqual({ statusCode: 200, body: 'Hello World' });
});
});
Integration Testing
- Using Serverless Offline:
serverless offline start &
sleep 5 # Wait for service to start
npm test
kill %1 # Stop service
- Using Docker Compose:
version: '3'
services:
serverless:
build: .
command: sls offline start
ports:
- "3000:3000"
volumes:
- .:/app
environment:
- NODE_ENV=test
Mock Services
- Mock DynamoDB:
// mockDynamo.js
const AWS = require('aws-sdk');
const dynamoDb = new AWS.DynamoDB.DocumentClient();
jest.mock('aws-sdk', () => ({
DynamoDB: {
DocumentClient: jest.fn(() => ({
get: jest.fn((params, callback) => {
callback(null, { Item: { id: params.Key.id, value: 'mocked' } });
}),
put: jest.fn((params, callback) => {
callback(null, {});
}),
})),
},
}));
module.exports = dynamoDb;
- Mock API Gateway:
// mockApiGateway.js
const mockResponse = (statusCode, body) => ({
statusCode,
body: JSON.stringify(body),
});
const mockLambdaContext = {
succeed: (response) => console.log('Success:', response),
fail: (error) => console.error('Error:', error),
};
module.exports = { mockResponse, mockLambdaContext };
Test Coverage
- Generate Coverage Report:
jest --coverage
- Configure Coverage Thresholds:
// jest.config.js
module.exports = {
// ...
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
};
Continuous Integration Testing
- GitHub Actions Configuration:
name: Node.js CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x, 16.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
- run: npm run coverage
- CodeCov Integration:
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
Test Data Management
- Using Factory Boy (JavaScript Version):
// factory.js
class Factory {
static createUser(overrides = {}) {
return {
id: Math.random().toString(36).substr(2, 9),
name: 'Test User',
email: 'test@example.com',
...overrides,
};
}
}
module.exports = Factory;
- Using Faker.js for Test Data:
const faker = require('faker');
const generateTestData = () => ({
user: {
id: faker.datatype.uuid(),
name: faker.name.findName(),
email: faker.internet.email(),
},
product: {
id: faker.datatype.uuid(),
name: faker.commerce.productName(),
price: faker.commerce.price(),
},
});
Test Environment Isolation
- Using Docker Compose for Isolated Environments:
version: '3'
services:
dynamodb:
image: amazon/dynamodb-local
ports:
- "8000:8000"
command: "-jar DynamoDBLocal.jar -sharedDb -inMemory"
redis:
image: redis
ports:
- "6379:6379"
- Using Testcontainers (Java) or dockerode (Node.js):
const Docker = require('dockerode');
const docker = new Docker();
async function startTestDynamoDB() {
const container = await docker.createContainer({
Image: 'amazon/dynamodb-local',
Cmd: ['-jar', 'DynamoDBLocal.jar', '-sharedDb', '-inMemory'],
HostConfig: {
PortBindings: {
'8000/tcp': [{ HostPort: '8000' }],
},
},
});
await container.start();
return `http://localhost:8000`;
}
Performance and Load Testing
- Using Artillery for Load Testing:
npm install -g artillery
# load-test.yml
config:
target: "http://localhost:3000"
phases:
- duration: 60
arrivalRate: 10
scenarios:
- flow:
- get:
url: "/api/hello"
Run test:
artillery run load-test.yml
- Using k6 for Performance Testing:
npm install -g k6
// script.js
import http from 'k6/http';
export default function () {
http.get('http://localhost:3000/api/hello');
}
Run test:
k6 run --vus 10 --duration 60s script.js
Security and Authentication Testing
- Simulating JWT Authentication:
// auth-mock.js
module.exports = (req, res, next) => {
req.headers.authorization = 'Bearer mock.jwt.token';
next();
};
- Testing IAM Permissions:
const AWS = require('aws-sdk-mock');
const AWS_SDK = require('aws-sdk');
AWS.mock('IAM', 'getRole', (params, callback) => {
callback(null, { Role: { Arn: 'arn:aws:iam::123456789012:role/MockRole' } });
});
// Test code
const iam = new AWS_SDK.IAM();
iam.getRole({ RoleName: 'TestRole' }, (err, data) => {
console.log(data); // { Role: { Arn: 'arn:aws:iam::123456789012:role/MockRole' } }
});
Event Trigger Testing
- Simulating S3 Events:
// s3-event.json
{
"Records": [
{
"eventVersion": "2.1",
"eventSource": "aws:s3",
"awsRegion": "us-east-1",
"eventTime": "2021-01-01T00:00:00.000Z",
"eventName": "ObjectCreated:Put",
"userIdentity": { "principalId": "AWS:AIDAJDPLRKLG7UEXAMPLE" },
"requestParameters": { "sourceIPAddress": "192.0.2.1" },
"responseElements": {
"x-amz-request-id": "EXAMPLE123456789",
"x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
},
"s3": {
"s3SchemaVersion": "1.0",
"configurationId": "testConfigRule",
"bucket": {
"name": "example-bucket",
"ownerIdentity": { "principalId": "A3NL1KOZZKExample" },
"arn": "arn:aws:s3:::example-bucket"
},
"object": {
"key": "test/key",
"size": 1024,
"eTag": "0123456789abcdef0123456789abcdef",
"sequencer": "0A1B2C3D4E5F678901"
}
}
}
]
}
- Testing DynamoDB Stream Events:
// dynamodb-event.json
{
"Records": [
{
"eventID": "1",
"eventName": "INSERT",
"eventVersion": "1.1",
"eventSource": "aws:dynamodb",
"awsRegion": "us-east-1",
"dynamodb": {
"ApproximateCreationDateTime": 1609459200,
"Keys": {
"id": {
"S": "123"
}
},
"NewImage": {
"id": {
"S": "123"
},
"name": {
"S": "Test Item"
}
},
"SequenceNumber": "111",
"SizeBytes": 26,
"StreamViewType": "NEW_AND_OLD_IMAGES"
},
"eventSourceARN": "arn:aws:dynamodb:us-east-1:123456789012:table/ExampleTable/stream/2021-01-01T00:00:00.000"
}
]
}
Error Handling Testing
- Simulating Lambda Errors:
// error-mock.js
module.exports = (err) => {
const error = new Error('Mocked Error');
error.code = 'MockedErrorCode';
error.statusCode = 500;
return error;
};
- Testing Retry Logic:
describe('Retry Logic', () => {
it('should retry failed operation', async () => {
const retryOperation = jest.fn()
.mockRejectedValueOnce(new Error('First failure'))
.mockResolvedValueOnce('Success');
const result = await withRetry(retryOperation, 3);
expect(result).toBe('Success');
expect(retryOperation).toHaveBeenCalledTimes(2);
});
});
Data Consistency Testing
- Transaction Testing:
describe('Database Transactions', () => {
it('should commit transaction on success', async () => {
const result = await performTransaction();
expect(result).toBe(true);
const dbData = await getFromDatabase();
expect(dbData).toEqual(expectedData);
});
it('should rollback transaction on failure', async () => {
await expect(failingTransaction()).rejects.toThrow();
const dbData = await getFromDatabase();
expect(dbData).not.toEqual(expectedData);
});
});
- Concurrency Testing:
describe('Concurrent Operations', () => {
it('should handle concurrent updates', async () => {
const promises = Array(10).fill().map(() => updateResource());
await Promise.all(promises);
const finalState = await getResource();
expect(finalState.version).toBe(10); // Assumes version increments per update
});
});
Test Environment Cleanup
- Automatic Test Data Cleanup:
afterEach(async () => {
await cleanupDatabase();
await deleteS3Objects();
});
async function cleanupDatabase() {
const items = await scanDatabase();
for (const item of items) {
await deleteFromDatabase(item.id);
}
}
async function deleteS3Objects() {
const objects = await listS3Objects();
for (const obj of objects) {
await deleteS3Object(obj.key);
}
}
- Using Docker Compose for Cleanup:
version: '3'
services:
test-cleanup:
image: alpine
volumes:
- ./test-data:/data
command: >
sh -c "rm -rf /data/* && echo 'Test data cleaned up'"
depends_on:
- app
Logging and Monitoring Testing
- Structured Logging:
// Structured logging with Winston
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'test.log' })
]
});
// Use in tests
logger.info('Test started', { testId: '123', timestamp: new Date() });
- Test Coverage Monitoring:
# Generate coverage report
jest --coverage
# Upload to Coveralls
cat ./coverage/lcov.info | coveralls
Performance Benchmark Testing
- Establishing Performance Baseline:
describe('Performance Baseline', () => {
it('should process 1000 requests in under 5 seconds', async () => {
const start = Date.now();
const promises = Array(1000).fill().map(() => makeRequest());
await Promise.all(promises);
const duration = Date.now() - start;
expect(duration).toBeLessThan(5000);
});
});
- Comparing Performance Across Versions:
# Benchmark with wrk
wrk -t12 -c400 -d30s http://localhost:3000/api/endpoint
# Record results and compare versions
Security Vulnerability Testing
- Static Code Analysis:
# Code quality check with ESLint
eslint . --ext .js,.ts
# Security scan with SonarQube
sonar-scanner
- Dynamic Security Testing:
# Security scan with OWASP ZAP
zap-cli quick-scan --self-contained --start --spider --ajax-spider --scanners all http://localhost:3000
# Manual testing with Burp Suite
Chaos Engineering Testing
- Simulating Failures:
// Simulate service failures with Chaos Monkey
const chaosMonkey = require('chaos-monkey');
describe('Chaos Engineering', () => {
it('should handle service failures gracefully', () => {
chaosMonkey.enable();
chaosMonkey.addRule({
probability: 0.5,
target: 'userService',
method: 'GET'
});
// Test code
await expect(makeRequest()).rejects.toThrow();
});
});
- Network Latency Testing:
# Simulate network latency with tc
sudo tc qdisc add dev eth0 root netem delay 100ms
# Run tests
jest
# Cleanup
sudo tc qdisc del dev eth0 root
Internationalization and Localization Testing
- Multi-Language Testing:
describe('Internationalization', () => {
it('should display messages in correct language', () => {
const messages = getMessages('es');
expect(messages.welcome).toBe('Bienvenido');
const messagesFr = getMessages('fr');
expect(messagesFr.welcome).toBe('Bienvenue');
});
});



