Module 3: Branching and Switching: Isolated Test Development

Learn to create and manage branches for developing new test features, fixing bugs, and experimenting with test frameworks without affecting the main test suite. Understand branch strategies specific to test automation projects and practice switching between different testing contexts.

Branch Strategies for Bug Fix Testing and Hotfixes

Why This Matters

Picture this: It’s Friday afternoon, and your team just discovered a critical bug in production. The automated tests passed, but they missed an edge case. You need to fix the failing test and add coverage for the bug—fast. But your main test branch is in the middle of a major refactoring. How do you isolate your urgent fix without disrupting ongoing work?

Or consider this common scenario: Two test engineers simultaneously discover different bugs in the same test file. Both create fixes independently. When it’s time to merge, whose changes take precedence? How do you ensure both fixes make it into the test suite without overwriting each other’s work?

This lesson solves these real-world challenges:

  • Isolation of urgent fixes: Keep production hotfixes separate from feature development, ensuring you can deploy critical test fixes without waiting for unrelated work to complete
  • Parallel bug fixing: Enable multiple team members to work on different bugs simultaneously without stepping on each other’s toes
  • Traceability: Link specific test changes directly to bug tickets or production issues through structured branch naming
  • Risk reduction: Test your bug fixes in isolation before merging them into the main suite, preventing new issues from affecting stable tests

When you’ll use these skills:

  • Responding to production incidents that require immediate test coverage
  • Fixing flaky or failing tests that are blocking the CI/CD pipeline
  • Adding regression tests for newly discovered bugs
  • Managing multiple bug fixes across different testing priorities
  • Coordinating test fixes with application hotfixes in synchronized releases

Common pain points this addresses:

  • “I can’t fix this urgent test failure without disrupting the experimental framework changes on main”
  • “My bug fix conflicts with someone else’s changes, and I don’t know which version to keep”
  • “We released a hotfix to production, but forgot to update the corresponding tests”
  • “Our test branch history is a mess—we can’t tell which commits fixed which bugs”

What You’ll Accomplish

By the end of this lesson, you’ll have a systematic approach to managing bug fixes and hotfixes in your test automation projects. You won’t just learn commands—you’ll understand the strategy behind branching decisions that professional test teams use daily.

Here’s how we’ll build this skill set:

  1. Creating dedicated bug fix branches from the main test suite: You’ll learn when and how to branch off from your stable test code, using hands-on examples that simulate real bug discovery scenarios. We’ll walk through the complete workflow from identifying a test gap to creating an isolated branch.

  2. Implementing hotfix branch workflows for urgent production issues: We’ll establish a clear distinction between regular bug fixes and critical hotfixes, including concrete examples of naming conventions and merge paths. You’ll practice the fast-track workflow that lets you get fixes to production quickly.

  3. Applying branch naming conventions for bug fixes and hotfixes: You’ll adopt industry-standard naming patterns (like bugfix/TEST-123-login-timeout and hotfix/PROD-456-payment-validation) that make your repository history self-documenting and searchable.

  4. Merging bug fix branches back to main while preserving test history: Through practical exercises, you’ll execute clean merges that maintain a clear record of what was fixed and why. We’ll cover both fast-forward and merge commit strategies appropriate for different scenarios.

  5. Handling conflicts when multiple bug fixes affect the same tests: You’ll work through realistic conflict scenarios where two bug fixes modify the same test file or test data. We’ll practice conflict resolution techniques specific to test code, ensuring both fixes are preserved correctly.

Each objective builds on the previous one, taking you from basic bug fix branching through complex multi-fix scenarios. You’ll work with concrete examples from a sample test automation project, making decisions about branch strategies just as you would in your daily work.

Let’s begin by establishing when to create a bug fix branch versus when to commit directly to main—a critical decision that sets the foundation for everything that follows.


Core Content

Core Content: Branch Strategies for Bug Fix Testing and Hotfixes

1. Core Concepts Explained

Understanding Bug Fix and Hotfix Workflows

Bug Fixes vs. Hotfixes: Key Differences

  • Bug Fixes: Non-critical issues discovered during normal development cycles

    • Follow standard development workflow
    • Go through full testing and review process
    • Merged into development/main branches normally
  • Hotfixes: Critical production issues requiring immediate attention

    • Bypass normal development cycle
    • Created directly from production/main branch
    • Require expedited testing and deployment

Branch Strategy Overview

graph TB
    A[Production/Main] --> B[Hotfix Branch]
    B --> C[Automated Tests]
    C --> D{Tests Pass?}
    D -->|Yes| E[Merge to Main]
    D -->|No| F[Fix & Retest]
    F --> C
    E --> G[Merge to Develop]
    
    H[Develop] --> I[Bugfix Branch]
    I --> J[Automated Tests]
    J --> K{Tests Pass?}
    K -->|Yes| L[Merge to Develop]
    K -->|No| M[Fix & Retest]
    M --> J

Setting Up Branch Strategies

Standard Naming Conventions:

  • Bugfix branches: bugfix/TICKET-123-description
  • Hotfix branches: hotfix/TICKET-456-critical-issue

Step 1: Configure Git Branch Protection

# Clone your repository
git clone https://github.com/your-repo/project.git
cd project

# View current branches
git branch -a

# Set up local branch tracking
git checkout -b develop origin/develop
git checkout -b main origin/main

Step 2: Create a Bugfix Branch

# Start from develop branch for non-critical bugs
git checkout develop
git pull origin develop

# Create bugfix branch
git checkout -b bugfix/login-validation-error

# Verify you're on the correct branch
git branch

Step 3: Create a Hotfix Branch

# Start from main/production for critical issues
git checkout main
git pull origin main

# Create hotfix branch
git checkout -b hotfix/payment-processing-failure

# Verify branch
git status

2. Practical Code Examples

Example 1: Bugfix Workflow with Automated Tests

Scenario: Login validation allows empty passwords

Step 1: Write/Update Test for Bug

// tests/login.spec.js
const { test, expect } = require('@playwright/test');

test.describe('Login Validation - Bugfix Tests', () => {
  
  test('should prevent login with empty password', async ({ page }) => {
    // Navigate to login page
    await page.goto('https://practiceautomatedtesting.com/login');
    
    // Fill username but leave password empty
    await page.fill('#username', 'testuser@example.com');
    await page.fill('#password', '');
    
    // Attempt login
    await page.click('button[type="submit"]');
    
    // Verify error message appears
    const errorMessage = await page.locator('.error-message');
    await expect(errorMessage).toBeVisible();
    await expect(errorMessage).toContainText('Password is required');
    
    // Verify user is NOT logged in
    await expect(page).not.toHaveURL(/.*dashboard/);
  });

  test('should show validation message immediately', async ({ page }) => {
    await page.goto('https://practiceautomatedtesting.com/login');
    
    // Focus and blur password field without entering data
    await page.click('#password');
    await page.click('#username');
    
    // Real-time validation should appear
    const validationHint = await page.locator('#password-error');
    await expect(validationHint).toBeVisible({ timeout: 1000 });
  });
});

Step 2: Run Tests on Bugfix Branch

# Install dependencies if needed
npm install

# Run specific test suite
npx playwright test tests/login.spec.js

# Expected output before fix:
$ npx playwright test tests/login.spec.js

Running 2 tests using 2 workers

  ✓ tests/login.spec.js:5:3 › should prevent login with empty password (1.2s)
  ✓ tests/login.spec.js:23:3 › should show validation message immediately (0.8s)

  2 passed (2.0s)

Step 3: Commit and Push Bugfix

# Stage changes
git add tests/login.spec.js
git add src/components/LoginForm.js

# Commit with descriptive message
git commit -m "fix: add password validation to prevent empty submissions

- Added client-side validation for password field
- Display error message for empty password
- Added automated tests to verify fix
- Fixes TICKET-123"

# Push bugfix branch
git push origin bugfix/login-validation-error

Example 2: Hotfix Workflow with Critical Test Coverage

Scenario: Payment processing fails for amounts over $1000

Step 1: Create Hotfix Test Suite

// tests/hotfix/payment-processing.spec.js
const { test, expect } = require('@playwright/test');

test.describe('HOTFIX: Payment Processing Critical Issue', () => {
  
  test.beforeEach(async ({ page }) => {
    // Login as test user
    await page.goto('https://practiceautomatedtesting.com/login');
    await page.fill('#username', 'testuser@example.com');
    await page.fill('#password', 'Password123!');
    await page.click('button[type="submit"]');
    await expect(page).toHaveURL(/.*dashboard/);
  });

  test('should process payment for amount over $1000', async ({ page }) => {
    // Navigate to payment page
    await page.goto('https://practiceautomatedtesting.com/checkout');
    
    // Enter high-value amount
    await page.fill('#payment-amount', '1500.00');
    await page.fill('#card-number', '4532015112830366');
    await page.fill('#cvv', '123');
    await page.fill('#expiry', '12/25');
    
    // Submit payment
    await page.click('#submit-payment');
    
    // Verify success (NOT error page)
    await expect(page.locator('.payment-success')).toBeVisible({ timeout: 10000 });
    await expect(page.locator('.error-message')).not.toBeVisible();
    
    // Verify confirmation number exists
    const confirmationNumber = await page.locator('#confirmation-number').textContent();
    expect(confirmationNumber).toMatch(/^[A-Z0-9]{10}$/);
  });

  test('should handle exactly $1000 payment', async ({ page }) => {
    await page.goto('https://practiceautomatedtesting.com/checkout');
    
    await page.fill('#payment-amount', '1000.00');
    await page.fill('#card-number', '4532015112830366');
    await page.fill('#cvv', '123');
    await page.fill('#expiry', '12/25');
    
    await page.click('#submit-payment');
    
    // Edge case: exactly $1000 should work
    await expect(page.locator('.payment-success')).toBeVisible({ timeout: 10000 });
  });

  test('should process payment for amount under $1000', async ({ page }) => {
    // Regression test - ensure small amounts still work
    await page.goto('https://practiceautomatedtesting.com/checkout');
    
    await page.fill('#payment-amount', '99.99');
    await page.fill('#card-number', '4532015112830366');
    await page.fill('#cvv', '123');
    await page.fill('#expiry', '12/25');
    
    await page.click('#submit-payment');
    
    await expect(page.locator('.payment-success')).toBeVisible();
  });
});

Step 2: Automated CI/CD Pipeline Configuration

# .github/workflows/hotfix-tests.yml
name: Hotfix Critical Tests

on:
  push:
    branches:
      - 'hotfix/**'
  pull_request:
    branches:
      - main

jobs:
  critical-tests:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run Playwright tests
        run: npx playwright test tests/hotfix/
      
      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: playwright-report
          path: playwright-report/
      
      - name: Notify on failure
        if: failure()
        run: |
          echo "::error::Hotfix tests failed! Do not merge until fixed."

Step 3: Execute Hotfix Workflow

# Ensure you're on hotfix branch
git branch
# * hotfix/payment-processing-failure

# Run tests locally first
npx playwright test tests/hotfix/payment-processing.spec.js

# Expected output after fix:
$ npx playwright test tests/hotfix/payment-processing.spec.js

Running 3 tests using 3 workers

  ✓ tests/hotfix/payment-processing.spec.js:10:3 › should process payment for amount over $1000 (3.5s)
  ✓ tests/hotfix/payment-processing.spec.js:32:3 › should handle exactly $1000 payment (2.8s)
  ✓ tests/hotfix/payment-processing.spec.js:47:3 › should process payment for amount under $1000 (2.1s)

  3 passed (8.4s)
# Commit hotfix
git add tests/hotfix/payment-processing.spec.js
git add src/services/PaymentProcessor.js

git commit -m "hotfix: fix payment processing for amounts over $1000

- Corrected decimal handling in payment processor
- Added validation for edge cases
- Added critical test coverage
- CRITICAL: Fixes production payment failures
- Fixes TICKET-456"

# Push to remote
git push origin hotfix/payment-processing-failure

Example 3: Merging Strategy with Test Verification

Creating Pull Request with Test Evidence

# Create PR using GitHub CLI (or web interface)
gh pr create \
  --base main \
  --head hotfix/payment-processing-failure \
  --title "HOTFIX: Fix payment processing for amounts over $1000" \
  --body "## Critical Issue
  
Payment processing fails for amounts exceeding $1000.

## Fix Applied
- Corrected decimal point handling in PaymentProcessor
- Added input validation for high-value transactions

## Test Coverage
- ✅ Payments over $1000
- ✅ Exact $1000 amount (edge case)
- ✅ Regression test for amounts under $1000

## Test Results
All tests passing. See automated test run in Actions tab.

## Deployment Plan
1. Merge to main immediately after approval
2. Deploy to production
3. Cherry-pick to develop branch"

Post-Merge: Sync Branches

# After hotfix is merged to main, update develop
git checkout develop
git pull origin develop

# Merge hotfix changes into develop
git merge main

# Resolve any conflicts if necessary
# Then push updated develop
git push origin develop

# Delete hotfix branch (cleanup)
git branch -d hotfix/payment-processing-failure
git push origin --delete hotfix/payment-processing-failure

3. Common Mistakes Section

Mistake 1: Creating Hotfix from Wrong Branch

Wrong:

# Creating hotfix from develop instead of main
git checkout develop
git checkout -b hotfix/critical-bug

Correct:

# Always create hotfix from production/main
git checkout main
git pull origin main
git checkout -b hotfix/critical-bug

Mistake 2: Insufficient Test Coverage for Hotfixes

Wrong:

// Only testing the exact failing scenario
test('should process $1500 payment', async ({ page }) => {
  // Single test case only
});

Correct:

// Test the fix, edge cases, AND regression scenarios
test.describe('Payment Processing Fix', () => {
  test('should process $1500 payment', async ({ page }) => { /*...*/ });
  test('should handle exactly $1000', async ({ page }) => { /*...*/ });
  test('should still work for small amounts', async ({ page }) => { /*...*/ });
});

Mistake 3: Forgetting to Sync Branches After Hotfix

Wrong:

# Merge hotfix to main, but forget develop
git checkout main
git merge hotfix/issue
# STOP - develop is now out of sync!

Correct:

# Complete workflow
git checkout main
git merge hotfix/issue
git push origin main

# Sync develop branch
git checkout develop
git merge main
git push origin develop

Debugging Tips

When Tests Fail on Hotfix Branch:

  1. Check branch base:

    git log --oneline --graph --all --decorate | head -20
    # Verify hotfix branched from main, not develop
    
  2. Run tests in isolation:

    # Test individual file
    npx playwright test tests/hotfix/specific-test.spec.js --debug
    
  3. Verify environment configuration:

    # Check if .env settings match production
    cat .env.test
    # Ensure API endpoints, credentials are production-like
    
  4. Compare with production state:

    # Ensure your local main matches remote production
    git fetch origin
    git diff origin/main main
    # Should show no differences
    

CI/CD Pipeline Failures:

# View detailed test results
gh run view --log-failed

# Download artifacts locally
gh run download <run-id>

# Re-run failed tests locally with same configuration
npx playwright test --config=playwright.ci.config.js

Key Takeaways:

  • Use bugfix/* branches from develop for normal bugs
  • Use hotfix/* branches from main for production emergencies
  • Always write comprehensive tests covering the fix, edge cases, and regressions
  • Sync branches after merging hotfixes to prevent divergence
  • Automate testing in CI/CD to catch issues before merging

Hands-On Practice

EXERCISE AND CONCLUSION

🎯 Hands-On Exercise

Exercise: Implement a Hotfix Testing Pipeline

Scenario: Your e-commerce application has a critical bug in production—the checkout button is broken. You need to create a hotfix branch, implement automated tests, and ensure the fix can be deployed safely without breaking existing functionality.

Task: Set up a complete hotfix workflow with automated testing that validates the bug fix while ensuring no regression in core features.

Step-by-Step Instructions

Step 1: Create Hotfix Branch Structure

# Clone the starter repository
git clone https://github.com/your-org/ecommerce-app.git
cd ecommerce-app

# Create hotfix branch from main/production
git checkout main
git pull origin main
git checkout -b hotfix/checkout-button-fix

Step 2: Set Up Test Structure

Create the following test files:

// tests/hotfix/checkout-button.test.js
const { test, expect } = require('@playwright/test');

test.describe('Hotfix: Checkout Button', () => {
  test('should display checkout button on cart page', async ({ page }) => {
    // TODO: Implement test that verifies the bug is fixed
  });

  test('should enable checkout with items in cart', async ({ page }) => {
    // TODO: Implement test for checkout functionality
  });

  test('should disable checkout with empty cart', async ({ page }) => {
    // TODO: Implement edge case test
  });
});
// tests/regression/core-features.test.js
const { test, expect } = require('@playwright/test');

test.describe('Regression: Core Features', () => {
  test('should allow adding items to cart', async ({ page }) => {
    // TODO: Implement regression test
  });

  test('should calculate correct cart total', async ({ page }) => {
    // TODO: Implement regression test
  });

  test('should maintain user session', async ({ page }) => {
    // TODO: Implement regression test
  });
});

Step 3: Configure CI/CD Pipeline

Create .github/workflows/hotfix-pipeline.yml:

name: Hotfix Testing Pipeline

on:
  push:
    branches:
      - 'hotfix/**'
  pull_request:
    branches:
      - main

jobs:
  hotfix-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run hotfix-specific tests
        run: npm run test:hotfix
      
      - name: Run regression test suite
        run: npm run test:regression
      
      - name: Run smoke tests
        run: npm run test:smoke
      
      - name: Generate test report
        if: always()
        run: npm run report:generate

  security-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run security audit
        run: npm audit --audit-level=moderate

Step 4: Implement Your Tests

Complete the TODO sections in the test files with:

  • Navigation to relevant pages
  • Element interactions
  • Proper assertions
  • Error handling

Step 5: Configure Test Scripts

Update package.json:

{
  "scripts": {
    "test:hotfix": "playwright test tests/hotfix --reporter=html",
    "test:regression": "playwright test tests/regression --reporter=html",
    "test:smoke": "playwright test tests/smoke --reporter=html",
    "test:all": "playwright test --reporter=html",
    "report:generate": "playwright show-report"
  }
}

Expected Outcome

After completing this exercise, you should have:

  1. ✅ A hotfix branch with focused test coverage
  2. ✅ Automated regression tests preventing breaking changes
  3. ✅ CI/CD pipeline that runs on hotfix branch pushes
  4. ✅ Clear test reports showing pass/fail status
  5. ✅ Documentation of the testing strategy in README.md

Solution Approach

Completed Test Example:

// tests/hotfix/checkout-button.test.js
const { test, expect } = require('@playwright/test');

test.describe('Hotfix: Checkout Button', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/cart');
    // Add test item to cart
    await page.goto('/products');
    await page.click('[data-testid="add-to-cart-btn"]');
    await page.goto('/cart');
  });

  test('should display checkout button on cart page', async ({ page }) => {
    const checkoutButton = page.locator('[data-testid="checkout-btn"]');
    await expect(checkoutButton).toBeVisible();
    await expect(checkoutButton).toBeEnabled();
  });

  test('should enable checkout with items in cart', async ({ page }) => {
    const checkoutButton = page.locator('[data-testid="checkout-btn"]');
    await checkoutButton.click();
    await expect(page).toHaveURL(/.*checkout/);
  });

  test('should disable checkout with empty cart', async ({ page }) => {
    await page.click('[data-testid="remove-item-btn"]');
    const checkoutButton = page.locator('[data-testid="checkout-btn"]');
    await expect(checkoutButton).toBeDisabled();
  });
});

Key Implementation Points:

  • Tests are isolated and independent
  • Use data-testid for stable selectors
  • Include positive and negative test cases
  • Regression tests cover critical user paths
  • Pipeline fails fast on test failures

🎓 Key Takeaways

  • Hotfix branches require focused testing - Prioritize tests that validate the specific bug fix and immediate surrounding functionality rather than running the entire test suite initially

  • Always include regression testing - Critical features must be retested during hotfixes to ensure the fix doesn’t introduce new bugs or break existing functionality

  • Automate hotfix pipelines differently - Hotfix branches need faster, more targeted CI/CD pipelines with smoke tests and critical path validation to enable rapid deployment

  • Branch strategy impacts test strategy - Different branch types (feature, hotfix, release) require different testing approaches—hotfixes need minimal but comprehensive coverage

  • Documentation is critical for hotfixes - Always document what was tested, what was skipped, and why to create an audit trail for urgent production fixes


🚀 Next Steps

What to Practice

  1. Set up a mock hotfix scenario - Create intentional bugs in a test application and practice the complete hotfix workflow
  2. Optimize test execution time - Experiment with parallel testing and test sharding for faster hotfix validation
  3. Create test templates - Build reusable test templates for common hotfix scenarios (UI bugs, API issues, data corruption)
  • Feature flag testing - Learn how to test features behind flags to enable safer deployments
  • Canary deployment testing - Explore progressive rollout strategies with automated testing
  • Post-deployment monitoring - Integrate synthetic monitoring and automated smoke tests in production
  • Emergency rollback procedures - Develop automated validation for rollback scenarios
  • Test impact analysis - Use tools to identify which tests are critical for specific code changes
  • Continuous Delivery patterns for hotfix management
  • Git workflow strategies for production support
  • Monitoring and observability in production environments
  • Incident response and testing procedures