Acceptance testing means at least three different things depending on who's doing it and when. UAT is business stakeholders confirming requirements before sign-off, ATDD is executable tests written before development starts, and functional acceptance testing is QA verifying the spec is fully implemented. The failure modes that matter: testing too late, involving the wrong people, and writing criteria vague enough to argue about.

What Acceptance Testing Actually Means

The term covers several related concepts:

User Acceptance Testing (UAT): The actual users (or business stakeholders) test the software in a staging environment to confirm it meets their requirements before sign-off. Acceptance test-driven development (ATDD): Requirements are written as executable tests before development starts. Tests pass when the feature is complete. Functional acceptance testing: QA verifies that all requirements from the spec are implemented and working correctly.

In practice, "acceptance testing" usually means one of two things: UAT done by business people, or functional acceptance testing done by QA against the formal requirements.

Acceptance Criteria: The Foundation

Acceptance testing starts with acceptance criteria — the specific conditions a feature must meet to be considered complete.

Bad acceptance criteria:
The login page should work properly.
Good acceptance criteria:
- Users can log in with a valid email and password and are redirected to the dashboard
- If email or password is wrong, an error message "Invalid email or password" appears
- After 5 failed attempts in 10 minutes, the account is locked and a "too many attempts" message appears
- Successful login creates a session that expires after 24 hours of inactivity
- "Forgot password" link on the login page sends a reset email within 2 minutes

Good criteria are specific, testable, and unambiguous.

Writing Acceptance Criteria with Gherkin

Gherkin is a structured language for writing acceptance criteria in a Given/When/Then format. It's readable by non-technical stakeholders and executable by tools like Cucumber:

Feature: User Login

  Scenario: Successful login with valid credentials
    Given I am on the login page
    When I enter "user@example.com" as email
    And I enter "ValidPass1" as password
    And I click the "Sign In" button
    Then I should be redirected to the dashboard
    And I should see "Welcome, Test User" in the header

  Scenario: Login with invalid credentials
    Given I am on the login page
    When I enter "user@example.com" as email
    And I enter "WrongPassword" as password
    And I click the "Sign In" button
    Then I should see "Invalid email or password"
    And I should remain on the login page

  Scenario: Account lockout after failed attempts
    Given I am on the login page
    When I enter "user@example.com" with wrong password 5 times
    Then I should see "Too many failed attempts. Try again in 10 minutes."
    And the Sign In button should be disabled

UAT Process: Step by Step

1. Define the test scope

Which features are included in this UAT cycle? Usually tied to a release or sprint.

2. Write test scenarios with business users

Involve the actual users or product owners in writing scenarios. They know what "correct" looks like from a business perspective.

3. Prepare the environment

UAT should run in an environment close to production — same data, same integrations, same configuration. A developer's laptop is not a UAT environment.

4. Create test data

Users need realistic test data that represents their actual use cases — not just "test@test.com" with "test123" as the password.

5. Execute tests

Ideally, real users run the tests. If not, QA runs them — but the acceptance criteria must be defined by business stakeholders, not QA.

6. Document results

Every test gets a pass/fail status. Failures get bug reports with steps to reproduce.

7. Fix and re-test

Bugs found in UAT go back to developers. QA re-tests once fixed.

8. Sign-off

Business stakeholders formally approve the release. Without sign-off, don't ship.

UAT vs QA Testing

| Aspect | QA Testing | UAT |

|--------|-----------|-----|

| Who does it | QA engineers | Business users / product owners |

| Goal | Find bugs | Confirm requirements met |

| Focus | Technical correctness | Business value |

| When | During development | Before release |

| Criteria | Test cases | Acceptance criteria |

| Sign-off | QA lead | Business stakeholder |

Both are necessary. QA catches the bugs before UAT so business users aren't doing basic bug finding.

Common Acceptance Testing Failure Modes

Testing too late:

UAT scheduled 2 days before release with no buffer for fixes. Every bug found is a crisis.

Wrong participants:

Having developers do UAT defeats the purpose. They know how the code works and won't test it like a user.

Vague acceptance criteria:

"The checkout should be smooth" leads to disagreements about what "smooth" means when bugs are found.

Testing in the wrong environment:

UAT on a development server with test data that doesn't reflect real volumes or integrations will miss real bugs.

No regression after fixes:

Bug found in UAT, fixed, deployed — but nobody checks if the fix broke something else.

Automated Acceptance Testing

You can automate acceptance tests with Playwright:

// acceptance/login.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Login — Acceptance Criteria', () => {
  test('AC1: Valid credentials redirect to dashboard', async ({ page }) => {
    await page.goto('/login');
    await page.fill('[data-testid="email"]', 'user@test.com');
    await page.fill('[data-testid="password"]', 'ValidPass1');
    await page.click('[data-testid="submit"]');
    
    await expect(page).toHaveURL('/dashboard');
    await expect(page.getByTestId('welcome')).toContainText('Welcome');
  });

  test('AC2: Invalid credentials show error message', async ({ page }) => {
    await page.goto('/login');
    await page.fill('[data-testid="email"]', 'user@test.com');
    await page.fill('[data-testid="password"]', 'WrongPassword');
    await page.click('[data-testid="submit"]');
    
    await expect(page.getByTestId('error-message')).toHaveText('Invalid email or password');
    await expect(page).toHaveURL('/login');
  });

  test('AC3: Session expires after 24h inactivity', async ({ page, context }) => {
    // This would typically be tested with mock time or API manipulation
    // showing the concept:
    await page.goto('/login');
    await page.fill('[data-testid="email"]', 'user@test.com');
    await page.fill('[data-testid="password"]', 'ValidPass1');
    await page.click('[data-testid="submit"]');
    
    // Simulate session expiry via API
    await page.request.post('/api/auth/expire-session');
    await page.reload();
    
    await expect(page).toHaveURL('/login');
  });
});

Acceptance Testing Checklist

Before starting UAT:

  • [ ] Acceptance criteria written and reviewed by business
  • [ ] Feature deployed to UAT environment (not dev or staging)
  • [ ] Test data prepared (realistic, not trivial)
  • [ ] Business users briefed on what to test
  • [ ] Bug tracking process established
  • [ ] Schedule includes time for fixes and re-testing
  • [ ] Sign-off authority identified

During UAT:

  • [ ] Tests executed against acceptance criteria
  • [ ] All failures documented with reproduction steps
  • [ ] Screenshots/recordings for complex issues
  • [ ] Severity assigned to each finding

After UAT:

  • [ ] All P1/P2 bugs fixed and re-tested
  • [ ] Business sign-off received in writing
  • [ ] Release notes updated with known P3/P4 issues

Summary

Acceptance testing confirms that software delivers the business value it was built for. Key points:

  • Based on acceptance criteria written by business stakeholders, not QA
  • UAT involves real users; functional acceptance testing is done by QA against requirements
  • Gherkin (Given/When/Then) makes criteria readable by both technical and non-technical stakeholders
  • Run in a production-like environment with realistic data
  • Requires formal sign-off before release
  • Can be automated with Playwright — especially valuable for regression on future changes

The difference between a QA sign-off and a business sign-off matters: QA says the code works; business says the feature solves their problem.

→ See also: Verification vs. Validation in Software Testing: What's the Difference? | How to Write a Test Plan: Template and Real Examples | Risk-Based Testing: Prioritizing What to Test When You Can't Test Everything