Module 9: Common Team Workflows: GitFlow and Trunk-Based Development

Implement professional Git workflows used by testing teams worldwide. Compare GitFlow (feature, develop, release, hotfix branches) with Trunk-Based Development for test projects. Learn when each workflow suits different team sizes and release cycles, with practical setup for test automation teams.

GitFlow Workflow: Building a Multi-Environment Test Suite

Why This Matters

The Real-World Challenge

Imagine this scenario: Your QA team is testing three different features simultaneously while supporting a production hotfix and preparing for next week’s release. Without a structured Git workflow, you face:

  • Test conflicts: Team members overwriting each other’s test code
  • Environment chaos: Tests meant for staging accidentally running in production
  • Release confusion: Uncertainty about which tests belong in the next release
  • Emergency paralysis: Unable to quickly patch production issues without disrupting ongoing development

Professional testing teams at companies like Atlassian, Microsoft, and Spotify use GitFlow to solve these exact problems. It’s not just about organizing code—it’s about enabling your team to work in parallel without stepping on each other’s toes.

When You’ll Use This Skill

GitFlow becomes essential when:

  • Your team has 3+ automation engineers working simultaneously on different features
  • You manage multiple environments (dev, QA, staging, production) with different test configurations
  • You follow scheduled releases (e.g., bi-weekly or monthly deployment cycles)
  • You need to support production while continuing development work
  • Your test suite is complex with thousands of tests requiring careful organization

Common Pain Points Addressed

This lesson directly tackles challenges that intermediate test engineers face daily:

  • “Our test suite broke production!” → Learn to isolate test changes until they’re proven stable
  • “I don’t know which branch to create my tests in” → Master clear branch naming and creation patterns
  • “Merging test code causes constant conflicts” → Implement strategies that minimize merge issues
  • “We can’t track which tests are release-ready” → Use release branches to gate-keep quality
  • “Hotfixes disrupt our entire testing workflow” → Create emergency patches without blocking development

What You’ll Accomplish

By the end of this lesson, you’ll have built a production-ready, multi-environment test suite using GitFlow. Here’s exactly what you’ll master:

🏗️ GitFlow Architecture Understanding

You’ll learn the complete GitFlow branching model and understand why each branch type exists. We’ll explore how main, develop, feature/*, release/*, and hotfix/* branches work together to create a resilient testing workflow. You’ll see real examples from industry teams and understand when GitFlow is the right choice (and when it’s overkill).

🔧 Hands-On GitFlow Setup

You’ll set up your own GitFlow repository from scratch, creating each branch type with proper naming conventions. We’ll initialize the structure, configure branch protection rules, and establish the foundation for your test automation project. By practicing the actual setup, you’ll be able to replicate this at work immediately.

🌍 Multi-Environment Configuration

You’ll implement environment-specific test configurations that automatically adapt based on which branch is running. Learn to manage separate config files for development, staging, and production environments, ensuring your tests use the correct URLs, credentials, and test data for each target environment.

🚀 Feature and Release Workflows

You’ll practice the complete feature development cycle: creating feature branches, writing tests, merging to develop, creating release branches, and finally promoting to production. We’ll walk through real scenarios like adding new test cases, updating existing tests, and handling failed tests during release preparation.

🚨 Hotfix Management

You’ll execute emergency hotfix workflows for production issues. Learn to create hotfix branches directly from main, write critical tests, and merge fixes back to both main and develop without disrupting ongoing work. This is the skill that makes you invaluable during production incidents.

⚖️ GitFlow vs. Trunk-Based Development

You’ll compare two professional workflows with objective criteria. Understand when GitFlow’s structure benefits large teams with scheduled releases versus when Trunk-Based Development’s simplicity serves continuous deployment teams better. You’ll get decision frameworks to recommend the right approach for your organization.

📋 Best Practices and Patterns

Throughout the lesson, you’ll learn industry-proven practices: branch naming conventions, commit message standards, code review checkpoints, merge strategies, and CI/CD integration patterns specific to test automation teams.


Let’s begin by understanding the GitFlow model and why it became the standard for structured software development teams…


Core Content

Core Content: GitFlow Workflow - Building a Multi-Environment Test Suite

1. Core Concepts Explained

Understanding GitFlow in Test Automation

GitFlow is a branching model that provides a structured approach to managing code versions. In test automation, GitFlow helps teams maintain separate test suites for different environments while ensuring code quality through systematic testing.

Key GitFlow Branches:

  • main - Production-ready code with stable tests
  • develop - Integration branch for ongoing development
  • feature/ - Individual test suite features
  • release/ - Pre-production testing branches
  • hotfix/ - Emergency fixes for production issues
graph LR
    A[main] --> B[hotfix/critical-test]
    A --> C[develop]
    C --> D[feature/login-tests]
    C --> E[feature/checkout-tests]
    C --> F[release/v1.0]
    F --> A
    B --> A
    D --> C
    E --> C

Multi-Environment Test Strategy

Different environments require different test configurations:

  • Development - Fast feedback, subset of tests
  • Staging - Full regression suite
  • Production - Smoke tests and monitoring

2. Setting Up GitFlow for Test Automation

Step 1: Initialize GitFlow Repository

# Create new test automation project
mkdir test-automation-suite
cd test-automation-suite

# Initialize git repository
git init

# Create initial structure
mkdir -p tests/{unit,integration,e2e}
mkdir -p config/{dev,staging,prod}

# Create initial commit
git add .
git commit -m "Initial test suite structure"

# Create develop branch
git checkout -b develop

Step 2: Install GitFlow Extension (Optional)

# For macOS
brew install git-flow

# For Ubuntu/Debian
sudo apt-get install git-flow

# For Windows (using Git Bash)
wget -q -O - --no-check-certificate https://raw.github.com/petervanderdoes/gitflow-avh/develop/contrib/gitflow-installer.sh install stable | bash

# Initialize GitFlow in your repository
git flow init
# Accept defaults: main, develop, feature/, release/, hotfix/, support/

3. Creating Environment-Specific Test Configurations

Step 3: Configure Environment Files

Create configuration files for each environment:

config/dev/test.config.js

module.exports = {
  baseURL: 'https://dev.practiceautomatedtesting.com',
  timeout: 30000,
  retries: 1,
  headless: false,
  slowMo: 50,
  testMatch: ['**/tests/unit/**/*.test.js', '**/tests/integration/**/*.test.js'],
  workers: 2
};

config/staging/test.config.js

module.exports = {
  baseURL: 'https://staging.practiceautomatedtesting.com',
  timeout: 60000,
  retries: 2,
  headless: true,
  testMatch: ['**/tests/**/*.test.js'],
  workers: 4
};

config/prod/test.config.js

module.exports = {
  baseURL: 'https://practiceautomatedtesting.com',
  timeout: 90000,
  retries: 3,
  headless: true,
  testMatch: ['**/tests/e2e/smoke/**/*.test.js'],
  workers: 1,
  failFast: true
};

Step 4: Create Environment Loader

config/environmentLoader.js

const path = require('path');

function loadConfig(environment = 'dev') {
  const validEnvs = ['dev', 'staging', 'prod'];
  
  if (!validEnvs.includes(environment)) {
    throw new Error(`Invalid environment: ${environment}. Must be one of: ${validEnvs.join(', ')}`);
  }
  
  const configPath = path.join(__dirname, environment, 'test.config.js');
  const config = require(configPath);
  
  console.log(`✓ Loaded ${environment} configuration`);
  return config;
}

module.exports = { loadConfig };

4. Building Tests with GitFlow Feature Branches

Step 5: Create a Feature Branch for New Tests

# Start a new feature for login tests
git checkout develop
git flow feature start login-tests

# Or manually:
# git checkout -b feature/login-tests develop

Step 6: Develop Environment-Aware Tests

tests/e2e/login.test.js

const { chromium } = require('playwright');
const { loadConfig } = require('../../config/environmentLoader');

describe('Login Tests', () => {
  let browser, context, page;
  const config = loadConfig(process.env.TEST_ENV || 'dev');
  
  beforeAll(async () => {
    browser = await chromium.launch({ headless: config.headless });
  });
  
  beforeEach(async () => {
    context = await browser.newContext();
    page = await context.newPage();
    await page.goto(config.baseURL);
  });
  
  afterEach(async () => {
    await context.close();
  });
  
  afterAll(async () => {
    await browser.close();
  });
  
  test('should successfully login with valid credentials', async () => {
    // Navigate to login
    await page.click('a[href*="login"]');
    
    // Fill credentials (use env-specific test data)
    await page.fill('#username', process.env.TEST_USER || 'testuser@example.com');
    await page.fill('#password', process.env.TEST_PASS || 'TestPass123');
    
    // Submit form
    await page.click('button[type="submit"]');
    
    // Verify login success
    await page.waitForSelector('.user-dashboard', { timeout: config.timeout });
    const welcomeText = await page.textContent('.welcome-message');
    expect(welcomeText).toContain('Welcome');
  });
  
  test('should show error with invalid credentials', async () => {
    await page.click('a[href*="login"]');
    await page.fill('#username', 'invalid@example.com');
    await page.fill('#password', 'wrongpassword');
    await page.click('button[type="submit"]');
    
    const errorMessage = await page.textContent('.error-message');
    expect(errorMessage).toContain('Invalid credentials');
  });
});

Step 7: Commit and Complete Feature

# Add tests to staging
git add tests/e2e/login.test.js

# Commit with descriptive message
git commit -m "Add login tests for multi-environment suite

- Implemented valid login test
- Implemented invalid login test
- Integrated environment-specific configuration"

# Complete feature (merges to develop)
git flow feature finish login-tests

# Or manually:
# git checkout develop
# git merge --no-ff feature/login-tests
# git branch -d feature/login-tests

5. Managing Releases Across Environments

Step 8: Create a Release Branch

# Start release preparation
git flow release start v1.0.0

# Or manually:
# git checkout -b release/v1.0.0 develop

Step 9: Configure CI/CD Pipeline for Multi-Environment Testing

.github/workflows/test-pipeline.yml

name: Multi-Environment Test Suite

on:
  push:
    branches: [ develop, 'release/**', main ]
  pull_request:
    branches: [ develop, main ]

jobs:
  test-dev:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/develop'
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
      - run: npm ci
      - name: Run development tests
        env:
          TEST_ENV: dev
          TEST_USER: ${{ secrets.DEV_TEST_USER }}
          TEST_PASS: ${{ secrets.DEV_TEST_PASS }}
        run: npm test

  test-staging:
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/heads/release/')
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
      - run: npm ci
      - name: Run staging tests
        env:
          TEST_ENV: staging
          TEST_USER: ${{ secrets.STAGING_TEST_USER }}
          TEST_PASS: ${{ secrets.STAGING_TEST_PASS }}
        run: npm test
      - name: Upload test reports
        uses: actions/upload-artifact@v3
        with:
          name: staging-test-reports
          path: test-results/

  test-prod-smoke:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
      - run: npm ci
      - name: Run production smoke tests
        env:
          TEST_ENV: prod
          TEST_USER: ${{ secrets.PROD_TEST_USER }}
          TEST_PASS: ${{ secrets.PROD_TEST_PASS }}
        run: npm test

Step 10: Finish Release

# Complete release testing in staging
TEST_ENV=staging npm test

# Finish release (merges to main and develop)
git flow release finish v1.0.0

# Or manually:
# git checkout main
# git merge --no-ff release/v1.0.0
# git tag -a v1.0.0 -m "Release version 1.0.0"
# git checkout develop
# git merge --no-ff release/v1.0.0
# git branch -d release/v1.0.0

6. Handling Hotfixes

Step 11: Create and Deploy Hotfix

# Critical bug found in production
git flow hotfix start fix-login-timeout

# Or manually:
# git checkout -b hotfix/fix-login-timeout main

tests/e2e/login.test.js (fix)

// Before - causing timeout
await page.waitForSelector('.user-dashboard', { timeout: config.timeout });

// After - more robust wait
await page.waitForSelector('.user-dashboard', { 
  timeout: config.timeout,
  state: 'visible'
});
await page.waitForLoadState('networkidle');
# Test hotfix against production config
TEST_ENV=prod npm test

# Commit fix
git add tests/e2e/login.test.js
git commit -m "Fix: Increase login test stability with network idle wait"

# Finish hotfix (merges to main and develop)
git flow hotfix finish fix-login-timeout

7. Package.json Scripts for Easy Execution

package.json

{
  "scripts": {
    "test": "jest",
    "test:dev": "TEST_ENV=dev npm test",
    "test:staging": "TEST_ENV=staging npm test",
    "test:prod": "TEST_ENV=prod npm test -- tests/e2e/smoke",
    "test:watch": "TEST_ENV=dev npm test -- --watch",
    "test:debug": "TEST_ENV=dev node --inspect-brk node_modules/.bin/jest --runInBand"
  }
}

Common Mistakes and Debugging

Mistake 1: Hardcoded Environment Values

❌ Wrong:

await page.goto('https://practiceautomatedtesting.com');

✅ Correct:

const config = loadConfig(process.env.TEST_ENV || 'dev');
await page.goto(config.baseURL);

Mistake 2: Merging Feature Directly to Main

❌ Wrong Workflow:

git checkout main
git merge feature/new-tests  # Bypasses develop

✅ Correct Workflow:

git checkout develop
git merge feature/new-tests
# Then later via release branch to main

Mistake 3: Not Testing in Target Environment

Always test in the environment matching your branch:

# On develop branch
TEST_ENV=dev npm test

# On release branch
TEST_ENV=staging npm test

# On main branch
TEST_ENV=prod npm test

Debugging Tips

Check current configuration:

# View loaded config
node -e "console.log(require('./config/environmentLoader').loadConfig('staging'))"

Verify branch strategy:

# Check current branch
git branch --show-current

# View branch history
git log --oneline --graph --all --decorate

Test environment isolation:

# Run with explicit environment
TEST_ENV=dev npm test -- --verbose

# Check environment variable
echo $TEST_ENV

This structured approach ensures your test automation suite scales across environments while maintaining code quality through GitFlow’s proven branching model.


Hands-On Practice

EXERCISE

🎯 Hands-On Exercise: Implement a Multi-Environment Test Suite with GitFlow

Objective

Build a complete GitFlow-based test automation framework that supports development, staging, and production environments with proper branching strategies and CI/CD integration.

Task

Create a test suite for a sample e-commerce application that follows GitFlow principles, implementing environment-specific configurations and automated test execution across different branches.

Prerequisites

  • Git installed
  • Node.js and npm (or Python and pip)
  • Basic understanding of CI/CD concepts
  • A GitHub/GitLab account

Step-by-Step Instructions

Step 1: Initialize Your Repository with GitFlow

# Create a new repository
git init test-automation-gitflow
cd test-automation-gitflow

# Initialize main branches
git checkout -b main
git checkout -b develop

Step 2: Create Project Structure

test-automation-gitflow/
├── config/
│   ├── dev.config.js
│   ├── staging.config.js
│   └── prod.config.js
├── tests/
│   ├── smoke/
│   ├── regression/
│   └── e2e/
├── utils/
│   └── environment.js
├── .github/
│   └── workflows/
│       ├── dev-tests.yml
│       ├── staging-tests.yml
│       └── prod-tests.yml
└── package.json

Step 3: Create Environment Configuration Files

config/dev.config.js

module.exports = {
  baseURL: 'https://dev.example.com',
  timeout: 30000,
  retries: 1,
  testTypes: ['unit', 'smoke', 'regression'],
  browserConfig: {
    headless: false,
    slowMo: 50
  }
};

config/staging.config.js

module.exports = {
  baseURL: 'https://staging.example.com',
  timeout: 45000,
  retries: 2,
  testTypes: ['smoke', 'regression', 'e2e'],
  browserConfig: {
    headless: true
  }
};

config/prod.config.js

module.exports = {
  baseURL: 'https://example.com',
  timeout: 60000,
  retries: 3,
  testTypes: ['smoke'],
  browserConfig: {
    headless: true
  }
};

Step 4: Create Environment Utility

utils/environment.js

const fs = require('fs');

class EnvironmentManager {
  constructor() {
    this.env = process.env.TEST_ENV || 'dev';
    this.config = this.loadConfig();
  }

  loadConfig() {
    const configPath = `./config/${this.env}.config.js`;
    if (!fs.existsSync(configPath)) {
      throw new Error(`Configuration file not found: ${configPath}`);
    }
    return require(`../config/${this.env}.config.js`);
  }

  getBaseURL() {
    return this.config.baseURL;
  }

  shouldRunTestType(testType) {
    return this.config.testTypes.includes(testType);
  }

  getConfig() {
    return this.config;
  }
}

module.exports = new EnvironmentManager();

Step 5: Create Sample Tests

tests/smoke/login.test.js

const env = require('../../utils/environment');

describe('Login Smoke Tests', () => {
  beforeAll(() => {
    if (!env.shouldRunTestType('smoke')) {
      return test.skip();
    }
  });

  test('should load login page', async () => {
    const baseURL = env.getBaseURL();
    console.log(`Testing on: ${baseURL}`);
    // Your test implementation
    expect(baseURL).toBeDefined();
  });

  test('should login with valid credentials', async () => {
    // Your test implementation
    expect(true).toBe(true);
  });
});

tests/regression/checkout.test.js

const env = require('../../utils/environment');

describe('Checkout Regression Tests', () => {
  beforeAll(() => {
    if (!env.shouldRunTestType('regression')) {
      return test.skip();
    }
  });

  test('should complete full checkout flow', async () => {
    // Your test implementation
    expect(true).toBe(true);
  });
});

Step 6: Create GitHub Actions Workflows

.github/workflows/dev-tests.yml

name: Development Tests

on:
  push:
    branches: [ develop, 'feature/**' ]
  pull_request:
    branches: [ develop ]

jobs:
  test:
    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 install
      
      - name: Run Development Tests
        env:
          TEST_ENV: dev
        run: npm test

.github/workflows/staging-tests.yml

name: Staging Tests

on:
  push:
    branches: [ release/** ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    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 install
      
      - name: Run Staging Tests
        env:
          TEST_ENV: staging
        run: npm test

.github/workflows/prod-tests.yml

name: Production Smoke Tests

on:
  push:
    branches: [ main ]
  schedule:
    - cron: '0 */6 * * *'  # Every 6 hours

jobs:
  test:
    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 install
      
      - name: Run Production Smoke Tests
        env:
          TEST_ENV: prod
        run: npm test
      
      - name: Notify on Failure
        if: failure()
        run: echo "Production tests failed! Alert team."

Step 7: Practice GitFlow Workflow

  1. Create a feature branch:
git checkout develop
git checkout -b feature/add-payment-tests
# Add new test file
git add .
git commit -m "feat: add payment gateway tests"
git push origin feature/add-payment-tests
  1. Create a release branch:
git checkout develop
git checkout -b release/1.0.0
# Update version numbers, final testing
git commit -am "chore: prepare release 1.0.0"
git push origin release/1.0.0
  1. Merge to main and tag:
git checkout main
git merge release/1.0.0
git tag -a v1.0.0 -m "Release version 1.0.0"
git push origin main --tags
  1. Create a hotfix:
git checkout main
git checkout -b hotfix/fix-login-bug
# Fix the bug
git commit -am "fix: resolve login validation issue"
git checkout main
git merge hotfix/fix-login-bug
git checkout develop
git merge hotfix/fix-login-bug

Expected Outcomes

You should have:

  • A complete GitFlow repository structure
  • Environment-specific configuration files
  • Tests that run conditionally based on environment
  • CI/CD workflows for each branch type
  • Successfully merged feature → develop → release → main
  • Created at least one hotfix branch

Verify Success:

# Check branch structure
git branch -a

# Verify workflows trigger
# Push to each branch type and check GitHub Actions

# Test environment switching locally
TEST_ENV=dev npm test
TEST_ENV=staging npm test
TEST_ENV=prod npm test

Solution Approach

  1. Environment Isolation: Each environment has distinct configurations preventing test interference
  2. Branch-Based Triggering: CI/CD workflows automatically run appropriate tests based on branch naming
  3. Progressive Testing: More comprehensive tests run in lower environments (dev/staging), while production runs critical smoke tests only
  4. Merge Strategy: Feature branches merge to develop, releases merge to both main and develop, hotfixes merge to both main and develop

CONCLUSION

🎓 Key Takeaways

  • GitFlow provides structured branching for managing test automation across multiple environments, ensuring the right tests run at the right stage of the deployment pipeline

  • Environment-specific configurations allow you to maintain different test suites, timeouts, and retry logic per environment without code duplication

  • CI/CD integration with GitFlow branches enables automatic test execution triggered by branch-specific events (feature → develop → release → main), creating a safety net throughout the development lifecycle

  • Hotfix workflows allow rapid production fixes while maintaining test integrity by automatically merging back to both main and develop branches

  • Progressive test strategies balance speed and coverage by running comprehensive tests in development/staging and focused smoke tests in production

🚀 Next Steps

Practice These Skills

  • Implement branch protection rules requiring test passage before merging
  • Add test result reporting and notifications (Slack, email, dashboards)
  • Create a hotfix scenario and practice the full workflow under time pressure
  • Set up test data management strategies per environment
  • Implement feature flags to control test execution dynamically
  • Trunk-Based Development as an alternative to GitFlow for faster-paced teams
  • Docker containerization for consistent test environments
  • Parallel test execution to reduce CI/CD pipeline time
  • Test data factories and fixtures for environment-specific data
  • Monitoring and observability integration with production smoke tests
  • Semantic versioning and automated changelog generation

Additional Resources

  • Practice with a real application (use test APIs like restful-booker or dummy JSON APIs)
  • Explore GitHub Actions marketplace for testing-specific actions
  • Study blue-green and canary deployment patterns with corresponding test strategies