agentsclimarketplace

Acceptance testing

Skill onfire7777/universal-ai-skills-library/skills/acceptance-testing

Router-first AI skill system for Codex, Claude, Cursor, Hermes, Paperclip, OpenCode, and local AI stacks: search, preflight-route, and load 1,812 skills on demand without duplicating the corpus.

Install
npx -y skills add onfire7777/universal-ai-skills-library --skill acceptance-testing

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 13 stars13 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use for verifying business requirements with executable specifications using BDD frameworks. Covers Cucumber (Java, JS, Ruby), SpecFlow/Reqnroll (C#), Behave (Python), Gauge (ThoughtWorks), and Godog (Go). Includes Gherkin feature files, step definitions, BDD workflow, and cross-language examples. USE FOR: Cucumber, SpecFlow, Reqnroll, Behave, Gauge, Godog, BDD test automation, step definitions, acceptance criteria verification, Given-When-Then execution, executable specifications, living documentation DO NOT USE FOR: writing Gherkin specification syntax (use specs/documentation/gherkin), writing Gauge specification syntax (use specs/documentation/gauge), unit testing (use unit-testing), API testing (use api-testing)

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

22.9 KB, as published. Nobody here has run it

Acceptance Testing (BDD)

Overview

Acceptance testing verifies that software meets business requirements by executing human-readable specifications. Behavior-Driven Development (BDD) bridges the gap between business stakeholders and developers through a collaborative workflow: business rules are written as executable specifications (feature files), automated with step definitions, and run as part of the CI pipeline.

For Gherkin specification syntax (Given/When/Then), see specs/documentation/gherkin. For Gauge specification syntax (Markdown specs), see specs/documentation/gauge.

BDD Workflow

  Collaborate          Specify            Automate           Validate
 ┌──────────┐     ┌──────────────┐    ┌──────────────┐   ┌──────────────┐
 │ Business  │────►│   Feature    │───►│    Step      │──►│  CI Pipeline │
 │ + Dev +   │     │   Files      │    │  Definitions │   │  Execution   │
 │ QA discuss│     │ (Gherkin/    │    │ (code that   │   │  + Reports   │
 │ examples  │     │  Gauge spec) │    │  runs specs) │   │              │
 └──────────┘     └──────────────┘    └──────────────┘   └──────────────┘
  1. Collaborate — Business, dev, and QA discuss requirements using concrete examples (Example Mapping, Three Amigos).
  2. Specify — Write executable specifications in Gherkin or Gauge Markdown format.
  3. Automate — Implement step definitions that translate specification steps into code.
  4. Validate — Run specifications as automated tests in CI; generate living documentation.

Cross-Platform Tools

ToolLanguagesSpec FormatStrengths
CucumberJava, JS/TS, RubyGherkin (.feature)Industry standard, largest ecosystem
SpecFlow / ReqnrollC# (.NET)Gherkin (.feature).NET-native, context injection, parallel execution
BehavePythonGherkin (.feature)Pythonic, fixtures, environment hooks
GaugeJava, JS, C#, Python, Ruby, GoMarkdown (.spec)Markdown specs, concept reuse, data tables
GodogGoGherkin (.feature)Go-native, godog.Suite, step definitions

Gherkin Feature File Example

# features/user-registration.feature
Feature: User Registration
  As a new visitor
  I want to create an account
  So that I can access personalized features

  Background:
    Given the registration page is displayed

  Scenario: Successful registration with valid details
    When I fill in the registration form with:
      | field    | value              |
      | name     | Jane Doe           |
      | email    | [email protected]   |
      | password | SecurePass123!     |
    And I accept the terms of service
    And I click the "Register" button
    Then I should see a welcome message "Welcome, Jane Doe!"
    And a confirmation email should be sent to "[email protected]"

  Scenario: Registration fails with duplicate email
    Given a user exists with email "[email protected]"
    When I fill in the registration form with:
      | field    | value              |
      | name     | Jane Doe           |
      | email    | [email protected]   |
      | password | SecurePass123!     |
    And I click the "Register" button
    Then I should see an error "An account with this email already exists"

  Scenario Outline: Registration fails with invalid input
    When I fill in "<field>" with "<value>"
    And I click the "Register" button
    Then I should see an error "<error>"

    Examples:
      | field    | value        | error                              |
      | name     |              | Name is required                   |
      | email    | not-an-email | Please enter a valid email address |
      | password | short        | Password must be at least 8 chars  |

Step Definitions by Language

JavaScript / TypeScript (Cucumber.js)

// features/step-definitions/registration.steps.js
const { Given, When, Then } = require("@cucumber/cucumber");
const { expect } = require("@playwright/test");

Given("the registration page is displayed", async function () {
    await this.page.goto("/register");
    await expect(this.page.locator("h1")).toHaveText("Create Account");
});

Given("a user exists with email {string}", async function (email) {
    // Seed the database or call an API to create the user
    await this.api.post("/test/seed-user", { email });
});

When("I fill in the registration form with:", async function (dataTable) {
    const rows = dataTable.rowsHash();
    for (const [field, value] of Object.entries(rows)) {
        await this.page.fill(`[name="${field}"]`, value);
    }
});

When("I fill in {string} with {string}", async function (field, value) {
    await this.page.fill(`[name="${field}"]`, value);
});

When("I accept the terms of service", async function () {
    await this.page.check("#terms-checkbox");
});

When("I click the {string} button", async function (buttonText) {
    await this.page.click(`button:has-text("${buttonText}")`);
});

Then("I should see a welcome message {string}", async function (message) {
    await expect(this.page.locator(".welcome-message")).toHaveText(message);
});

Then(
    "a confirmation email should be sent to {string}",
    async function (email) {
        // Verify via test email service (e.g., Mailhog, Mailtrap)
        const emails = await this.mailService.getEmails(email);
        expect(emails.length).toBeGreaterThan(0);
        expect(emails[0].subject).toContain("Confirm your account");
    }
);

Then("I should see an error {string}", async function (errorMessage) {
    await expect(this.page.locator(".error-message")).toHaveText(errorMessage);
});
// features/support/world.js
const { setWorldConstructor, Before, After } = require("@cucumber/cucumber");
const { chromium } = require("@playwright/test");

class CustomWorld {
    constructor() {
        this.page = null;
        this.browser = null;
        this.api = null;
    }
}

setWorldConstructor(CustomWorld);

Before(async function () {
    this.browser = await chromium.launch();
    const context = await this.browser.newContext();
    this.page = await context.newPage();
});

After(async function () {
    await this.browser?.close();
});

C# (SpecFlow / Reqnroll)

// Features/StepDefinitions/RegistrationSteps.cs
using Reqnroll;               // or TechTalk.SpecFlow for SpecFlow
using Microsoft.Playwright;
using Xunit;

[Binding]
public class RegistrationSteps
{
    private readonly IPage _page;
    private readonly ApiClient _api;

    // Context injection — Reqnroll/SpecFlow injects shared state automatically
    public RegistrationSteps(BrowserContext context, ApiClient api)
    {
        _page = context.Page;
        _api = api;
    }

    [Given("the registration page is displayed")]
    public async Task GivenTheRegistrationPageIsDisplayed()
    {
        await _page.GotoAsync("/register");
        var heading = await _page.TextContentAsync("h1");
        Assert.Equal("Create Account", heading);
    }

    [Given("a user exists with email {string}")]
    public async Task GivenAUserExistsWithEmail(string email)
    {
        await _api.PostAsync("/test/seed-user", new { Email = email });
    }

    [When("I fill in the registration form with:")]
    public async Task WhenIFillInTheRegistrationFormWith(Table table)
    {
        foreach (var row in table.Rows)
        {
            var field = row["field"];
            var value = row["value"];
            await _page.FillAsync($"[name=\"{field}\"]", value);
        }
    }

    [When("I accept the terms of service")]
    public async Task WhenIAcceptTheTermsOfService()
    {
        await _page.CheckAsync("#terms-checkbox");
    }

    [When("I click the {string} button")]
    public async Task WhenIClickTheButton(string buttonText)
    {
        await _page.ClickAsync($"button:has-text(\"{buttonText}\")");
    }

    [Then("I should see a welcome message {string}")]
    public async Task ThenIShouldSeeAWelcomeMessage(string message)
    {
        var text = await _page.TextContentAsync(".welcome-message");
        Assert.Equal(message, text);
    }

    [Then("a confirmation email should be sent to {string}")]
    public async Task ThenAConfirmationEmailShouldBeSentTo(string email)
    {
        var emails = await _api.GetAsync<List<Email>>($"/test/emails?to={email}");
        Assert.NotEmpty(emails);
        Assert.Contains("Confirm your account", emails[0].Subject);
    }

    [Then("I should see an error {string}")]
    public async Task ThenIShouldSeeAnError(string errorMessage)
    {
        var text = await _page.TextContentAsync(".error-message");
        Assert.Equal(errorMessage, text);
    }
}

Python (Behave)

# features/steps/registration_steps.py
from behave import given, when, then
from playwright.sync_api import expect


@given("the registration page is displayed")
def step_registration_page(context):
    context.page.goto("/register")
    expect(context.page.locator("h1")).to_have_text("Create Account")


@given('a user exists with email "{email}"')
def step_user_exists(context, email):
    context.api.post("/test/seed-user", json={"email": email})


@when("I fill in the registration form with")
def step_fill_form(context):
    for row in context.table:
        field = row["field"]
        value = row["value"]
        context.page.fill(f'[name="{field}"]', value)


@when('I fill in "{field}" with "{value}"')
def step_fill_field(context, field, value):
    context.page.fill(f'[name="{field}"]', value)


@when("I accept the terms of service")
def step_accept_terms(context):
    context.page.check("#terms-checkbox")


@when('I click the "{button_text}" button')
def step_click_button(context, button_text):
    context.page.click(f'button:has-text("{button_text}")')


@then('I should see a welcome message "{message}"')
def step_welcome_message(context, message):
    expect(context.page.locator(".welcome-message")).to_have_text(message)


@then('a confirmation email should be sent to "{email}"')
def step_confirmation_email(context, email):
    emails = context.mail_service.get_emails(email)
    assert len(emails) > 0
    assert "Confirm your account" in emails[0]["subject"]


@then('I should see an error "{error_message}"')
def step_error_message(context, error_message):
    expect(context.page.locator(".error-message")).to_have_text(error_message)
# features/environment.py
from playwright.sync_api import sync_playwright
import requests


def before_all(context):
    context.playwright = sync_playwright().start()
    context.browser = context.playwright.chromium.launch()


def before_scenario(context, scenario):
    context.browser_context = context.browser.new_context()
    context.page = context.browser_context.new_page()
    context.api = requests.Session()
    context.api.base_url = "http://localhost:3000"


def after_scenario(context, scenario):
    context.browser_context.close()


def after_all(context):
    context.browser.close()
    context.playwright.stop()

Gauge

Overview

Gauge (by ThoughtWorks) uses Markdown-based specification files instead of Gherkin. Specifications are written as natural-language steps in .spec files, with reusable abstractions called "concepts".

For Gauge specification syntax details, see specs/documentation/gauge.

Gauge Spec Example

# User Registration

## Successful registration with valid details
Tags: registration, smoke

* Navigate to the registration page
* Fill in registration form with name "Jane Doe" and email "[email protected]" and password "SecurePass123!"
* Accept terms of service
* Click the "Register" button
* Verify welcome message "Welcome, Jane Doe!" is displayed
* Verify confirmation email sent to "[email protected]"

## Registration fails with duplicate email
Tags: registration, negative

* Ensure user exists with email "[email protected]"
* Navigate to the registration page
* Fill in registration form with name "Jane Doe" and email "[email protected]" and password "SecurePass123!"
* Click the "Register" button
* Verify error message "An account with this email already exists" is displayed

Gauge Concept (Reusable Step Group)

# Register a new user with <name> and <email>
* Navigate to the registration page
* Fill in registration form with name <name> and email <email> and password "DefaultPass123!"
* Accept terms of service
* Click the "Register" button

Gauge Step Implementation (JavaScript)

// tests/step_implementations/registration.js
const { Step, BeforeSuite, AfterSuite } = require("gauge-ts");
const { openBrowser, closeBrowser, goto, write, click, into, textBox, text, checkBox } = require("taiko");

Step("Navigate to the registration page", async () => {
    await goto("http://localhost:3000/register");
});

Step(
    "Fill in registration form with name <name> and email <email> and password <password>",
    async (name, email, password) => {
        await write(name, into(textBox({ name: "name" })));
        await write(email, into(textBox({ name: "email" })));
        await write(password, into(textBox({ name: "password" })));
    }
);

Step("Accept terms of service", async () => {
    await checkBox({ id: "terms-checkbox" }).check();
});

Step("Click the <buttonText> button", async (buttonText) => {
    await click(buttonText);
});

Step("Verify welcome message <message> is displayed", async (message) => {
    assert(await text(message).exists());
});

Step("Verify error message <message> is displayed", async (message) => {
    assert(await text(message).exists());
});

Godog (Go)

Overview

Godog is the official Cucumber BDD framework for Go. It uses standard Gherkin feature files with step definitions written in Go.

Step Definitions

// features/registration_test.go
package features

import (
    "context"
    "fmt"
    "net/http"
    "testing"

    "github.com/cucumber/godog"
    "github.com/playwright-community/playwright-go"
)

type registrationContext struct {
    page    playwright.Page
    browser playwright.Browser
}

func (rc *registrationContext) theRegistrationPageIsDisplayed() error {
    _, err := rc.page.Goto("http://localhost:3000/register")
    if err != nil {
        return err
    }
    heading, err := rc.page.TextContent("h1")
    if err != nil {
        return err
    }
    if heading != "Create Account" {
        return fmt.Errorf("expected 'Create Account', got '%s'", heading)
    }
    return nil
}

func (rc *registrationContext) aUserExistsWithEmail(email string) error {
    resp, err := http.Post(
        "http://localhost:3000/test/seed-user",
        "application/json",
        strings.NewReader(fmt.Sprintf(`{"email":"%s"}`, email)),
    )
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    return nil
}

func (rc *registrationContext) iFillInWithValue(field, value string) error {
    return rc.page.Fill(fmt.Sprintf(`[name="%s"]`, field), value)
}

func (rc *registrationContext) iClickTheButton(buttonText string) error {
    return rc.page.Click(fmt.Sprintf(`button:has-text("%s")`, buttonText))
}

func (rc *registrationContext) iShouldSeeAnError(message string) error {
    text, err := rc.page.TextContent(".error-message")
    if err != nil {
        return err
    }
    if text != message {
        return fmt.Errorf("expected error '%s', got '%s'", message, text)
    }
    return nil
}

func InitializeScenario(ctx *godog.ScenarioContext) {
    rc := &registrationContext{}

    ctx.Before(func(ctx context.Context, sc *godog.Scenario) (context.Context, error) {
        pw, _ := playwright.Run()
        browser, _ := pw.Chromium.Launch()
        page, _ := browser.NewPage()
        rc.browser = browser
        rc.page = page
        return ctx, nil
    })

    ctx.After(func(ctx context.Context, sc *godog.Scenario, err error) (context.Context, error) {
        rc.browser.Close()
        return ctx, nil
    })

    ctx.Step(`^the registration page is displayed$`, rc.theRegistrationPageIsDisplayed)
    ctx.Step(`^a user exists with email "([^"]*)"$`, rc.aUserExistsWithEmail)
    ctx.Step(`^I fill in "([^"]*)" with "([^"]*)"$`, rc.iFillInWithValue)
    ctx.Step(`^I click the "([^"]*)" button$`, rc.iClickTheButton)
    ctx.Step(`^I should see an error "([^"]*)"$`, rc.iShouldSeeAnError)
}

func TestFeatures(t *testing.T) {
    suite := godog.TestSuite{
        ScenarioInitializer: InitializeScenario,
        Options: &godog.Options{
            Format:   "pretty",
            Paths:    []string{"features"},
            TestingT: t,
        },
    }
    if suite.Run() != 0 {
        t.Fatal("non-zero exit code from godog")
    }
}

CI Integration

Cucumber.js CI Pipeline

# .github/workflows/acceptance.yml
name: Acceptance Tests
on: [push, pull_request]

jobs:
  acceptance-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npm start &
      - run: npx wait-on http://localhost:3000
      - run: npx cucumber-js --format json:results/cucumber.json --format html:results/report.html
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: acceptance-results
          path: results/

Reqnroll / SpecFlow CI Pipeline

# .github/workflows/acceptance-dotnet.yml
name: Acceptance Tests (.NET)
on: [push, pull_request]

jobs:
  acceptance-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 8.0.x
      - run: dotnet restore
      - run: dotnet build --no-restore
      - run: dotnet test --no-build --logger "trx;LogFileName=results.trx"
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: acceptance-results
          path: "**/*.trx"

Gauge CI Pipeline

# .github/workflows/gauge.yml
name: Gauge Acceptance Tests
on: [push, pull_request]

jobs:
  gauge-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm install -g @getgauge/cli
      - run: gauge install js
      - run: gauge install html-report
      - run: npm ci
      - run: gauge run specs/ --env staging
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: gauge-report
          path: reports/

Project Structure Patterns

Cucumber / SpecFlow / Behave (Gherkin-based)

project/
  features/
    user-registration.feature
    checkout.feature
    step-definitions/          # or step_definitions/ or StepDefinitions/
      registration.steps.js    # JS
      RegistrationSteps.cs     # C#
      registration_steps.py    # Python
    support/
      world.js                 # JS — World/context setup
      hooks.js                 # JS — Before/After hooks
    environment.py             # Python — Behave hooks
  cucumber.js                  # Cucumber.js config
  reqnroll.json                # Reqnroll config

Gauge

project/
  specs/
    user-registration.spec
    checkout.spec
  concepts/
    register-user.cpt
    login.cpt
  tests/
    step_implementations/
      registration.js
      checkout.js
  env/
    default/
      default.properties
    staging/
      staging.properties

Best Practices

Specification Writing

  • Write specifications before code — they are requirements, not afterthoughts.
  • Use concrete examples with real data, not abstract placeholders.
  • Keep scenarios independent — each scenario should set up its own state (Background for shared setup).
  • Limit scenarios to 3-8 steps; if longer, consider splitting or using higher-level steps.
  • Use Scenario Outlines / data tables for testing multiple input variations of the same behavior.
  • Write specifications in domain language, not UI implementation language ("I register an account" not "I fill in #field-23").

Step Definitions

  • Keep step definitions thin — delegate to page objects, API clients, or service layers.
  • Reuse step definitions across features; avoid duplicating similar steps.
  • Use parameterized steps with Cucumber Expressions or regex for flexible matching.
  • Use hooks (Before/After) for setup and teardown, not step definitions.

BDD Workflow

  • Hold Three Amigos sessions (business, dev, QA) before writing specifications.
  • Use Example Mapping to discover acceptance criteria, rules, and edge cases.
  • Treat feature files as living documentation — keep them up to date with the codebase.
  • Generate HTML reports for stakeholder review (Cucumber HTML, Gauge HTML Report, SpecFlow+ LivingDoc).

Automation

  • Run acceptance tests in CI on every PR for critical flows.
  • Use dedicated test environments with seeded data for reliable execution.
  • Tag scenarios (@smoke, @regression, @wip) to run subsets in different pipeline stages.
  • Parallelize scenario execution where frameworks support it (Reqnroll, Cucumber with parallel profiles).
  • Separate fast API-level acceptance tests from slow browser-level acceptance tests.

Anti-Patterns to Avoid

  • Incidental details — Don't include UI selectors or technical steps in feature files.
  • Imperative style — Prefer declarative steps ("I register an account") over imperative steps ("I type 'Jane' into the name field, then I type...").
  • Coupled scenarios — Don't rely on scenario execution order; each scenario must be independent.
  • Testing implementation — Test business behavior, not internal code structure.
  • Too many scenarios — If a feature has 50+ scenarios, the feature is too broad; split it.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.