Gauge: Markdown-Based Acceptance Testing Framework
Gauge is an open-source acceptance testing framework from Thoughtworks that uses plain Markdown for test specifications instead of Gherkin. It supports step implementations in Java, JavaScript, Python, Ruby, C#, and Go, runs tests in parallel natively, and integrates with the Taiko browser automation library for end-to-end web testing.
Key Takeaways
Gauge specs are plain Markdown. Scenarios are .spec files that render as readable documentation in any Markdown viewer, with no special syntax beyond standard headings and bullets.
Steps are language-agnostic. The same .spec file can run against Java, Python, JavaScript, or Go step implementations — you choose the language per project, not per file.
Concepts let you compose steps. A Concept is a reusable step that expands into a sequence of lower-level steps, letting you build higher-level vocabulary without duplicating implementation code.
Data tables drive parametrized runs. Inline Markdown tables in spec files feed rows as parameters to scenarios, with Gauge executing one run per row automatically.
Taiko is the natural browser companion. Gauge and Taiko are both Thoughtworks projects; together they provide a full end-to-end stack where specs are written in Markdown and browser interactions in expressive JavaScript.
What Makes Gauge Different?
Most BDD frameworks use Gherkin as their specification language. Gauge takes a different approach: specs are valid Markdown files. A Gauge spec looks like this:
# User Registration
## Register with a valid email
* Navigate to the registration page
* Enter email "user@example.com" and password "Str0ngP@ss"
* Submit the registration form
* Verify the welcome email was sent to "user@example.com"The # heading is the spec (equivalent to Gherkin's Feature:). The ## heading is a scenario. Bullet points (*) are steps. That is the entire syntax.
Because this is standard Markdown, every spec file renders perfectly in GitHub, GitLab, Confluence, and any documentation tool — no special plugin required. Product managers see a readable document; the test runner sees executable steps.
Installation
Gauge has a single cross-platform CLI:
# macOS
brew install gauge
# Linux / Windows (via curl)
curl -SsL https://downloads.gauge.org/stable | sh
# Verify
gauge versionAfter installing the CLI, add the language plugin for your implementation language:
gauge install js # JavaScript / Node.js
gauge install java # Java
gauge install python # Python
gauge install dotnet # C#
gauge install ruby # RubyInitialise a new project:
gauge init js # Creates project scaffold for JavaScriptThe scaffold creates:
project/
├── specs/ ← your Markdown spec files
├── step_implementations/← your step code
├── env/
│ └── default/
│ └── default.properties
├── manifest.json
└── package.json (JS projects)Writing Specs
Basic Spec
# Product Search
## Search returns relevant results
* Open the shop at "https://shop.example.com"
* Search for "mechanical keyboard"
* Verify at least 3 results are shown
* Verify the first result contains "keyboard" in its title
## Search with no results shows empty state
* Open the shop at "https://shop.example.com"
* Search for "xyzzy_nonexistent_item_12345"
* Verify the empty state message "No products found" is displayedTags
Tags filter which specs or scenarios to run:
# Checkout Flow
tags: checkout, regression
## Pay with credit card
tags: payment, smoke
* Add "Widget" to the cart
* Proceed to checkout
* Pay with card "4111111111111111"
* Confirm order placed successfullyRun only smoke tests:
gauge run --tags smoke specs/Context Steps
Steps placed before the first ## heading run before every scenario in the spec — equivalent to Gherkin's Background::
# Order Management
* Log in as "admin@example.com" with password "AdminPass1"
## View pending orders
* Navigate to the orders dashboard
* Verify pending orders tab is activeImplementing Steps (JavaScript)
// step_implementations/search.js
const { Step, BeforeSuite, AfterSuite, BeforeScenario } = require('gauge-js');
const { openBrowser, goto, write, press, text, closeBrowser } = require('taiko');
BeforeSuite(async () => {
await openBrowser({ headless: true });
});
AfterSuite(async () => {
await closeBrowser();
});
Step('Open the shop at <url>', async (url) => {
await goto(url);
});
Step('Search for <query>', async (query) => {
await write(query, { into: 'search input' });
await press('Enter');
});
Step('Verify at least <count> results are shown', async (count) => {
// Taiko's $ returns elements matching the selector
const results = await $('[data-testid="product-card"]').elements();
assert.ok(results.length >= parseInt(count, 10),
`Expected >= ${count} results, got ${results.length}`);
});
Step('Verify the first result contains <text> in its title', async (expected) => {
const firstTitle = await $('[data-testid="product-card"]:first-child h2').text();
assert.ok(
firstTitle.toLowerCase().includes(expected.toLowerCase()),
`First result title "${firstTitle}" does not contain "${expected}"`
);
});Step parameters are denoted by <angle brackets> in both the spec file and the Step() call. Gauge matches them by position, not name.
Implementing Steps (Python)
# step_impl/search.py
from getgauge.python import step, before_suite, after_suite
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = None
@before_suite
def before_suite_hook():
global driver
options = webdriver.ChromeOptions()
options.add_argument("--headless")
driver = webdriver.Chrome(options=options)
@after_suite
def after_suite_hook():
driver.quit()
@step("Open the shop at <url>")
def open_shop(url):
driver.get(url)
@step("Search for <query>")
def search(query):
search_box = driver.find_element(By.CSS_SELECTOR, "input[type='search']")
search_box.send_keys(query)
search_box.submit()
@step("Verify at least <count> results are shown")
def verify_result_count(count):
results = driver.find_elements(By.CSS_SELECTOR, "[data-testid='product-card']")
assert len(results) >= int(count), f"Expected >= {count} results, got {len(results)}"Concepts: Composable Steps
A Concept is a reusable, higher-level step defined in a .cpt file. It expands into a sequence of lower-level steps at runtime — no implementation code required.
<!-- concepts/auth.cpt -->
# Log in as <user> with password <password>
* Navigate to "/login"
* Enter <user> in the email field
* Enter <password> in the password field
* Click the "Sign In" button
* Verify the dashboard is visibleNow any spec can use Log in as "alice@example.com" with password "Secret1" as a single step. The concept expands transparently in the report so you can see every sub-step that ran.
Concepts are composable — a concept can call other concepts. This lets you build a vocabulary that matches your application's domain without pushing complexity into implementation code.
Data Tables
Inline data tables drive parametrized scenario execution:
## Calculate shipping costs
| destination | weight_kg | expected_cost |
|-------------|-----------|---------------|
| domestic | 1 | 5.00 |
| domestic | 10 | 12.00 |
| international | 1 | 18.00 |
| international | 10 | 45.00 |
* Add a package weighing <weight_kg> kg destined for <destination>
* Calculate shipping
* Verify shipping cost is <expected_cost>Gauge executes the scenario once per table row, substituting column values into the <angle bracket> parameters. The report shows each row as a separate execution.
For data that should be shared across multiple scenarios in a spec, use an external CSV file:
## Process bulk orders
table: data/orders.csv
* Load the order batch from table
* Process all orders
* Verify zero failuresParallel Execution
Gauge supports parallel spec execution out of the box:
# Run with 4 parallel streams
gauge run --parallel --n 4 specs/
# Run in parallel, grouped by spec file
gauge run --parallel --strategy=eager specs/Two strategies:
lazy(default): Gauge assigns the next available spec to a free worker as each worker becomes free.eager: Specs are distributed upfront in a round-robin fashion.
For browser tests, each parallel stream should get its own browser instance. Use BeforeSpec hooks with worker-local state:
const { BeforeSpec, AfterSpec } = require('gauge-js');
const { openBrowser, closeBrowser } = require('taiko');
BeforeSpec(async () => {
await openBrowser({ headless: true });
});
AfterSpec(async () => {
await closeBrowser();
});Taiko Integration
Taiko is a Node.js browser automation library purpose-built to work with Gauge. Its API is designed to be readable in the context of a Gauge step implementation:
const {
openBrowser, goto, click, write, text,
dropDown, checkBox, fileField, closeBrowser,
screenshot, $, $$
} = require('taiko');
Step('Fill in the checkout form with <name>, <address>, <city>', async (name, address, city) => {
await write(name, { into: 'Full name' });
await write(address, { into: 'Street address' });
await write(city, { into: 'City' });
});
Step('Select <country> from the country dropdown', async (country) => {
await dropDown('Country').select(country);
});
Step('Take a screenshot named <filename>', async (filename) => {
await screenshot({ path: `screenshots/${filename}.png` });
});Taiko's "smart selectors" try to locate elements by label text, placeholder, ARIA role, and visible text before falling back to CSS selectors, which makes step implementations robust against minor UI changes.
Integration with HelpMeTest
Gauge excels at capturing acceptance criteria as Markdown documents that evolve alongside the product. But acceptance tests in a CI pipeline only run when someone pushes code. Production incidents often happen between deploys.
HelpMeTest fills this gap by running natural-language test scenarios against your deployed application on a continuous schedule — every few minutes if needed. Because HelpMeTest uses the same Given/When/Then vocabulary as Gauge specs, porting a critical Gauge acceptance scenario to a HelpMeTest monitor is a copy-paste exercise.
The result: Gauge protects the development workflow, HelpMeTest protects production uptime. Both speak the same language; neither needs the other's infrastructure.
When to Choose Gauge Over Gherkin-Based Tools
- Your product team prefers Markdown over the formal Given/When/Then structure
- You want spec files that render without a plugin in GitHub or Confluence
- You need multi-language step implementations (polyglot microservices with shared specs)
- You want first-class parallel execution without plugins
- You are building a Node.js web app and want a cohesive stack with Taiko
Gauge is a younger project with a smaller ecosystem than Cucumber or SpecFlow. If your team has deep Gherkin investment or needs the breadth of the Cucumber plugin ecosystem, Cucumber remains the safer choice. For greenfield projects and teams comfortable with Markdown, Gauge's cleaner syntax and native parallelism are genuine advantages.
Summary
Gauge brings acceptance testing into the Markdown world. Specs are readable documents first and executable tests second. Multi-language step support, native parallel execution, composable Concepts, and seamless Taiko integration make it a compelling alternative to Gherkin-based tools for teams starting fresh. Extend your coverage to production with HelpMeTest, and your Gauge specs become the foundation of an end-to-end quality strategy that covers both development and live monitoring.