Skip to content

Testing Patterns

Effective testing strategies and common pitfalls to avoid.

Happy Path First

Start with Success

Always begin with the expected success case:

gherkin
Scenario: Successful purchase
  Given a customer with valid payment
  When they purchase a product
  Then the order should be confirmed

Then Add Edge Cases

gherkin
Scenario: Purchase with insufficient funds
  Given a customer with insufficient funds
  When they try to purchase
  Then purchase should fail
  And customer should see error message

Scenario: Purchase with invalid card
  Given a customer with expired card
  When they try to purchase
  Then purchase should fail
  And customer should be asked to update payment

Boundary Testing

Test Limits

gherkin
Scenario Outline: Password validation
  When I enter password "<password>"
  Then validation should <result>

  Examples: Length boundaries
    | password    | result |
    | 1234567     | fail   |  # Below minimum (7)
    | 12345678    | pass   |  # At minimum (8)
    | 123456789   | pass   |  # Above minimum (9)

Test Transitions

gherkin
Scenario Outline: Discount tiers
  Given a customer with <orders> previous orders
  Then they should be in <tier> tier

  Examples:
    | orders | tier     |
    | 0      | bronze   |
    | 4      | bronze   |  # Just below silver
    | 5      | silver   |  # Exactly at silver
    | 6      | silver   |  # Just above threshold
    | 19     | silver   |  # Just below gold
    | 20     | gold     |  # Exactly at gold

Common Pitfalls

Avoid Implementation Details

gherkin
# Avoid: Exposes implementation
Given a record in users table with id=1
When I send POST to /api/orders

# Prefer: Business language
Given a user exists
When the user places an order

Don't Over-Specify

gherkin
# Avoid: Too specific
Given a user "John" with email "john@test.com" and role "user"
  and created at "2024-01-01" and verified at "2024-01-02"

# Prefer: Only what matters
Given a verified user exists

Don't Test Implementation

gherkin
# Avoid: Testing how it works
Given UserService is configured
When UserService.createUser() is called
Then database insert should occur

# Prefer: Testing what it does
Given I am on registration page
When I register with valid details
Then I should be logged in

Avoid Conditional Logic in Steps

php
// Avoid: Conditional in step
#[When('the user {action} the item')]
public function userAction(string $action, User $user, Item $item): void
{
    if ($action === 'buys') {
        $user->buy($item);
    } elseif ($action === 'returns') {
        $user->return($item);
    }
}

// Prefer: Separate steps
#[When('the user buys the item')]
public function buyItem(User $user, Item $item): void
{
    $user->buy($item);
}

#[When('the user returns the item')]
public function returnItem(User $user, Item $item): void
{
    $user->return($item);
}

State Management

Don't Share State Between Scenarios

php
// Avoid: Class-level state
class CartSteps
{
    private static Cart $cart; // Shared across scenarios!

    #[Given('an empty cart')]
    public function emptyCart(): void
    {
        self::$cart = new Cart();
    }
}

// Prefer: Return for injection
class CartSteps
{
    #[Given('an empty cart')]
    public function emptyCart(): Cart
    {
        return new Cart();
    }
}

Clean Up After Scenarios

php
#[Given('a temporary file exists')]
public function tempFile(): string
{
    $path = storage_path('temp/test-file.txt');
    file_put_contents($path, 'test');

    // Register cleanup
    afterEach(fn () => @unlink($path));

    return $path;
}

Error Scenarios

Test Error Messages

gherkin
Scenario: Login with wrong password
  Given a user with email "john@test.com"
  When I login with email "john@test.com" and password "wrong"
  Then I should see error "Invalid credentials"
  And I should not be logged in

Test Error Recovery

gherkin
Scenario: Retry after payment failure
  Given a customer with temporarily unavailable payment
  When they try to purchase
  Then they should see "Payment failed, please try again"
  When they try to purchase again
  Then the order should be confirmed

Documentation in Tests

Self-Documenting Scenarios

gherkin
Feature: Credit Application Processing
  Agricultural credit applications are evaluated based on
  the farmer's land holdings. The minimum land requirement
  is 5 hectares per 10,000 TL of requested credit.

  Scenario: Application approved with sufficient land
    # A farmer with 50 hectares can apply for up to 100,000 TL
    Given a farmer with 50 hectares of land
    When they apply for 100000 TL credit
    Then the application should be approved

Document Complex Steps

php
/**
 * Calculates eligibility based on land-to-credit ratio.
 *
 * Business rule: 5 hectares per 10,000 TL
 * Example: 50 hectares → eligible for 100,000 TL
 */
#[When('the farmer applies for {amount} TL credit')]
public function apply(Farmer $farmer, int $amount): CreditApplication
{
    return CreditService::evaluate($farmer, $amount);
}

Anti-Pattern Summary

Anti-PatternProblemSolution
Implementation detailsBrittle tests, hard to readUse business language
Over-specificationUnnecessary complexityOnly include relevant data
Dependent scenariosCan't run in isolationEach scenario is independent
Shared mutable stateRace conditions, flaky testsReturn objects for injection
Giant scenariosHard to debug, unclear intentOne behavior per scenario
Conditional stepsComplex, hard to maintainSeparate specific steps

Released under the MIT License.