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
- 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
- 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
- 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
- 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
- Environment Isolation: Each environment has distinct configurations preventing test interference
- Branch-Based Triggering: CI/CD workflows automatically run appropriate tests based on branch naming
- Progressive Testing: More comprehensive tests run in lower environments (dev/staging), while production runs critical smoke tests only
- 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
Related Topics to Explore
- 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