An expandable, fast, and easy-to-use automation framework built with the Cucumber language. MAF supports API testing and SQL testing in a simple manner, allows the usage of modules in isolation as well as in an integrated fashion, and utilizes the Cucumber language to clearly articulate intent while preserving test data for further debugging and record-keeping. The framework allows the integration of custom modules and provides a core to enable simple integration between components.
Module | Status |
---|---|
API | |
AWS | |
MySQL | |
SFTP | |
PostgreSQL | |
DefaultSQL | |
Preprocessor | |
Validations |
- Node.js v22 or greater
-
Create a new npm project:
npm init
-
Install any of the required dependencies for testing:
npm i @cucumber/cucumber npm i @ln-maf/aws npm i @ln-maf/validations npm i @ln-maf/api npm i @ln-maf/mysql npm i @ln-maf/core
(Optional) Install multiple-cucumber-html-reporter for reporting
npm i multiple-cucumber-html-reporter
-
Create a features directory and step definitions:
mkdir features
Create
imports.js
with the following content:require('@ln-maf/core/parameter_types') require('@ln-maf/aws') require('@ln-maf/validations') require('@ln-maf/api') require('@ln-maf/mysql')
-
Configure your
package.json
to use Cucumber:{ "scripts": { "test": "npx cucumber-js -f json:test/report/report.json $EXTRAS" } }
-
Create the test report directory:
mkdir -p test/report
Now you can run npm test
to execute tests and npx multiReport
to generate an HTML report.
MAF uses a global storage system called items
to share data between test steps. Think of it as a shared memory where you can store values with names (keys) and retrieve them later in any step.
When you store data using MAF, it gets saved in a global results
object that persists throughout your entire test scenario. This allows you to:
- Store data from API responses, database queries, or manual assignments
- Access data in subsequent steps using the stored item name
- Pass data between different types of operations (API → Database → Validation)
Basic Assignment:
When set "name" to "John"
When set "age" to 25
When set "isActive" to true
From API Responses:
When perform api request:
"""
{
"url": "https://api.example.com/users/1",
"method": "GET"
}
"""
# The response automatically gets stored as item "response"
From Database Queries:
When mysql query from string "SELECT name FROM users WHERE id = 1" is run
# The query results get stored as item "queryResult"
Direct Item Comparison:
Then item "name" is equal to "John"
Then item "age" is equal to 25
Then item "isActive" is equal to true
Template Literals (Variable Substitution):
# Using ${itemName} syntax to inject stored values
Then "${name} Doe" is equal to "John Doe"
Then "User ${name} is ${age} years old" is equal to "User John is 25 years old"
In API Requests:
When set "userId" to 123
When perform api request:
"""
{
"url": "https://api.example.com/users/${userId}",
"method": "GET"
}
"""
In SQL Queries:
When set "userEmail" to "john@example.com"
When mysql query from string "SELECT * FROM users WHERE email = '${userEmail}'" is run
Complex Objects:
When set "user" to:
"""
{
"name": "John",
"email": "john@example.com",
"preferences": {
"theme": "dark",
"notifications": true
}
}
"""
# Access nested properties
Then item "user.name" is equal to "John"
Then item "user.preferences.theme" is equal to "dark"
JavaScript Expressions:
When set "tomorrow" to "${new Date(Date.now() + 86400000).toISOString().split('T')[0]}"
When set "randomId" to "${Math.floor(Math.random() * 1000)}"
When set "calculation" to "${5 * 10 + 2}"
Data Pipeline Example:
# Step 1: Create a user via API
When set "newUser" to:
"""
{
"name": "Jane Smith",
"email": "jane@example.com"
}
"""
When perform api request:
"""
{
"url": "https://api.example.com/users",
"method": "POST",
"body": "${newUser}"
}
"""
# Step 2: Extract the created user ID from response
When set "userId" to "${response.id}"
# Step 3: Verify in database
When mysql query from string "SELECT * FROM users WHERE id = ${userId}" is run
Then item "queryResult[0].email" is equal to "jane@example.com"
# Step 4: Update the user
When perform api request:
"""
{
"url": "https://api.example.com/users/${userId}",
"method": "PUT",
"body": {
"name": "Jane Johnson"
}
}
"""
# Step 5: Verify the update
Then item "response.name" is equal to "Jane Johnson"
The first example uses a {jsonObject} to reference stored items. The second example uses template literals to inject values directly into strings. Both approaches are provided by the validations module.
You can inline JavaScript code in feature files, removing the need to create full step definitions for common functions:
# If today was January 16, 2024
When set "currentDate" to "${DateTime.now().toFormat('yyyy-MM-dd')}"
Then item "currentDate" is equal to "2024-01-16"
Note: Only simple functions that don't require external dependencies should be inlined. Luxon is available in the core module (moment is deprecated), so you can use Luxon functions like DateTime.now().toFormat('yyyy-MM-dd')
in feature files.
Create features/HelloWorldAPI.feature
:
Feature: View the text "Hello World"
Scenario: Hello World
When perform api request:
"""
{
"url": "http://www.mocky.io/v2/",
"api": "5ec540242f00004cb1dc30dd",
"method": "GET"
}
"""
Then status ok
And item "response" is equal to "Hello World"
➜ mafMonoRepo git:(master) npm test
...
1 scenario (1 passed)
3 steps (3 passed)
0m00.360s
=====================================================================================
Multiple Cucumber HTML report generated in:
$HOME/mafMonoRepo/test/report/index.html
=====================================================================================
The framework includes several modules that can be used independently or together:
-
Core - Core functionality and utilities. Essential for setting up custom cucumber steps using the
MAFWhen
function. Also handles template literal parsing for easy variable access within strings. -
Validations
- Helper cucumber steps for setting objects and performing validations. Includes steps like
Then item "a" is equal to 5
andWhen "Hello World" is base64 encoded
. -
API
- Cucumber steps for performing API calls and testing.
-
AWS
- Cucumber steps for AWS services including S3, DynamoDB, SQS, Lambda, ECS, and CloudWatch.
-
MySQL
- Cucumber steps for MySQL database testing.
-
PostgreSQL
- Cucumber steps for PostgreSQL database testing.
-
DefaultSQL
- Common SQL functionality used to create other SQL modules. Reference implementation for creating new SQL database modules.
-
SFTP
- SFTP file transfer capabilities for testing file operations.
-
Preprocessor
- Feature file preprocessing that adds information to feature files before execution.
- Node.js v22 or greater
- Docker (required for AWS, MySQL, and PostgreSQL module testing)
MAF is organized as a monorepo using Lerna for package management. This structure allows for:
- Independent versioning: Each package can have its own version number
- Cross-package dependencies: Packages can depend on each other within the monorepo
- Unified testing: Run tests across all packages with a single command
- Coordinated releases: Version and publish related packages together
The monorepo contains the following packages:
@ln-maf/core
- Core functionality and utilities@ln-maf/api
- API testing capabilities@ln-maf/aws
- AWS services integration (S3, DynamoDB, SQS, Lambda, ECS, CloudWatch)@ln-maf/validations
- Data validation and assertion steps@ln-maf/mysql
- MySQL database testing@ln-maf/postgresql
- PostgreSQL database testing@ln-maf/defaultSQL
- Common SQL functionality@ln-maf/sftp
- SFTP file transfer capabilities@ln-maf/preprocessor
- Feature file preprocessing
For information on how to add a module, see AddModule. This guide will help you create new step definitions that can be used in your project.
The easiest way to test individual packages is using npm workspaces:
# Test a specific package
npm test -w packages/api
npm test -w packages/aws
npm test -w packages/mysql
npm test -w packages/validations
# etc.
# Run all tests across all packages
npm test
Advantages of npm workspaces:
- Faster execution for individual packages
- Automatic dependency resolution
- Built-in Docker management for AWS tests
- No need to manually start/stop services
For quick development and testing:
-
Make your changes to any package
-
Test the specific package you modified:
npm test -w packages/PACKAGE_NAME
-
Test all packages before committing:
npm test
lerna run test
- Run tests across all packages in parallellerna run test --scope=@ln-maf/PACKAGE_NAME
- Test a specific package using Lernalerna run test:coverage
- Run coverage tests across all packages (if available)lerna run test --stream
- Run tests with streaming output for better debugging
lerna bootstrap
- Install dependencies and link cross-dependencieslerna clean
- Remove node_modules from all packageslerna ls
- List all packages in the monorepolerna changed
- Show packages that have changed since last releaselerna version (prerelease|release|patch|minor|major)
- Version packages that have changedlerna publish
- Publish packages to npm registry