Module 1: Introduction to Git and Version Control for Testing
Understand why version control is critical for test automation projects. Learn the fundamental concepts of Git, set up your environment, and make your first commits with test scripts. Explore how Git solves common problems test engineers face: tracking changes in test cases, collaborating on automation frameworks, and maintaining test data.
Branching Strategies for Test Development and Bug Fixes
Why This Matters
Picture this: You’re in the middle of developing a complex end-to-end test suite when production breaks. A critical bug is discovered, and the existing tests need an urgent fix. Meanwhile, your half-finished feature tests aren’t ready for production. Without a proper branching strategy, you face an impossible choice: abandon your work-in-progress, or delay the critical fix.
This scenario plays out daily in test automation teams worldwide. As test suites grow in complexity and teams expand, managing multiple streams of test development becomes chaotic without structured branching strategies. You might encounter:
- Incomplete test code accidentally deployed to shared environments, causing false failures for the entire team
- Lost work when team members overwrite each other’s test modifications
- Inability to hotfix production issues because test code is mixed with experimental features
- Difficulty tracking which tests correspond to which application features or releases
- Merge nightmares when multiple testers modify the same page objects or test data files
Branching strategies solve these problems by creating isolated workspaces for different types of test development activities. Just as developers use branches to manage application code, test engineers need branching workflows tailored to test automation challenges: managing test data variations, coordinating framework updates, and aligning test coverage with release cycles.
When you’ll use this skill:
- Developing new automated tests without disrupting stable test suites
- Fixing broken tests urgently while feature work continues
- Preparing test suites for major releases with specific test configurations
- Collaborating with multiple test engineers on shared frameworks
- Maintaining separate test environments (staging, production, performance testing)
- Coordinating test updates with application feature branches
What You’ll Learn
This lesson provides a comprehensive guide to branching strategies specifically designed for test automation projects. Unlike generic Git branching tutorials, we focus on real-world scenarios test engineers face daily.
Here’s how we’ll address each learning objective:
Understanding Branching Strategy Importance - We’ll explore why ad-hoc branching fails in test automation and examine case studies showing how proper strategies prevent common disasters like test suite instability and deployment blockers.
Creating Feature Branches - You’ll learn hands-on commands to create isolated branches for new test development, whether you’re adding a new test suite, updating page objects, or implementing framework enhancements. We’ll cover practical examples like feature/login-tests
and feature/api-test-framework
.
Implementing Hotfix Workflows - We’ll walk through the complete lifecycle of emergency test fixes: creating hotfix branches from stable code, making targeted fixes, and fast-tracking them to production while keeping feature work separate.
Using Release Branches - You’ll learn to create and manage release branches that stabilize test suites before major deployments, including how to handle last-minute test adjustments and configuration changes specific to release versions.
Applying Naming Conventions - We’ll establish clear, team-friendly naming patterns for test automation branches (like test/feature-name
, hotfix/bug-id
, release/version-number
) that make your repository navigable and self-documenting.
Merging with Different Strategies - You’ll practice both merge commits and rebase workflows, understanding when each approach benefits test automation projects, and how to maintain clean, readable test history.
Resolving Merge Conflicts - Through practical examples, you’ll learn to resolve conflicts in test scripts, page objects, test data files, and configuration files—the most common conflict scenarios in test automation.
Branch Cleanup and Maintenance - Finally, you’ll master the housekeeping tasks that keep repositories healthy: identifying obsolete branches, safely deleting merged branches, and maintaining a lean, organized branch structure.
By the end of this lesson, you’ll have a proven framework for organizing test development that scales from solo projects to large, distributed test engineering teams.
Core Content
Core Content: Branching Strategies for Test Development and Bug Fixes
Core Concepts Explained
Understanding Branching in Test Automation
Branching strategies are essential workflows that help teams organize test development, bug fixes, and feature testing in parallel without disrupting the main codebase. A well-structured branching strategy ensures:
- Isolation: Tests can be developed without affecting stable test suites
- Collaboration: Multiple testers can work simultaneously
- Quality Control: Changes are reviewed before merging
- Traceability: Clear history of what tests were added when and why
Common Branching Models for Test Automation
1. Feature Branch Strategy
Create a dedicated branch for each new test feature or test suite:
# Create and switch to a new feature branch
git checkout -b feature/login-automation-tests
# View your current branch
git branch
When to use: Adding new test scenarios, implementing new page objects, or creating test data utilities.
2. Bugfix Branch Strategy
Create branches specifically for fixing failing tests or test infrastructure issues:
# Create a bugfix branch
git checkout -b bugfix/fix-checkout-test-timeout
# Alternative naming convention
git checkout -b fix/broken-payment-verification
When to use: Fixing flaky tests, correcting selectors, or resolving test environment issues.
3. Hotfix Branch Strategy
For urgent production test failures that need immediate attention:
# Create hotfix branch from main
git checkout main
git checkout -b hotfix/critical-login-test-failure
When to use: Production test failures blocking releases, critical regression suite issues.
Step-by-Step: Implementing a Feature Branch Workflow
Step 1: Create Your Feature Branch
# Ensure you're starting from the latest main branch
git checkout main
git pull origin main
# Create your feature branch
git checkout -b feature/search-functionality-tests
# Verify you're on the correct branch
git branch --show-current
Step 2: Develop Your Tests
Create your test file with proper structure:
// tests/search.spec.js
const { test, expect } = require('@playwright/test');
test.describe('Search Functionality Tests', () => {
test.beforeEach(async ({ page }) => {
// Navigate to practice site
await page.goto('https://practiceautomatedtesting.com');
});
test('should display search results for valid product', async ({ page }) => {
// Locate and interact with search input
const searchInput = page.locator('#search-input');
await searchInput.fill('laptop');
// Submit search
await page.locator('#search-button').click();
// Verify results are displayed
const results = page.locator('.search-results .product-item');
await expect(results).toHaveCount(5, { timeout: 5000 });
// Verify product titles contain search term
const firstProduct = results.first();
await expect(firstProduct).toContainText('laptop', { ignoreCase: true });
});
test('should show no results message for invalid search', async ({ page }) => {
const searchInput = page.locator('#search-input');
await searchInput.fill('xyznonexistent123');
await page.locator('#search-button').click();
// Verify no results message
const noResultsMsg = page.locator('.no-results-message');
await expect(noResultsMsg).toBeVisible();
await expect(noResultsMsg).toContainText('No products found');
});
});
Step 3: Commit Your Changes Incrementally
# Check what files have changed
git status
# Add specific test files
git add tests/search.spec.js
# Create meaningful commit messages
git commit -m "Add search functionality test scenarios
- Add test for valid search with results verification
- Add test for empty results handling
- Include proper waits and assertions"
# Push your branch to remote
git push origin feature/search-functionality-tests
Step-by-Step: Bug Fix Workflow
Step 1: Identify and Create Bug Fix Branch
# Create branch from main
git checkout main
git pull origin main
git checkout -b bugfix/fix-flaky-checkout-test
# Document the issue you're fixing in your first commit
Step 2: Fix the Test Issue
// tests/checkout.spec.js
// BEFORE (Flaky test with timing issues)
test('should complete checkout process', async ({ page }) => {
await page.goto('https://practiceautomatedtesting.com/cart');
await page.click('#checkout-button');
await page.fill('#email', 'test@example.com');
await page.click('#submit-order');
// ❌ Flaky: No wait for order confirmation
expect(page.url()).toContain('/order-success');
});
// AFTER (Stable test with proper waits)
test('should complete checkout process', async ({ page }) => {
await page.goto('https://practiceautomatedtesting.com/cart');
// Wait for page to be fully loaded
await page.waitForLoadState('networkidle');
// Click checkout and wait for navigation
await Promise.all([
page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
page.click('#checkout-button')
]);
// Fill form with proper validation
await page.fill('#email', 'test@example.com');
await expect(page.locator('#email')).toHaveValue('test@example.com');
// Submit and wait for success page
await Promise.all([
page.waitForURL('**/order-success', { timeout: 10000 }),
page.click('#submit-order')
]);
// ✅ Explicit verification with wait
await expect(page.locator('.success-message')).toBeVisible();
expect(page.url()).toContain('/order-success');
});
Step 3: Verify and Commit the Fix
# Run the specific test multiple times to verify stability
npx playwright test tests/checkout.spec.js --repeat-each=5
# If all runs pass, commit the fix
git add tests/checkout.spec.js
git commit -m "Fix flaky checkout test with proper waits
- Add networkidle wait before interactions
- Use Promise.all for navigation clicks
- Add explicit visibility checks for confirmation
- Increase timeout for order processing
Fixes: Issue #234"
git push origin bugfix/fix-flaky-checkout-test
Creating Pull Requests for Test Changes
# After pushing your branch, create a PR
# This is typically done via GitHub/GitLab UI
<!-- SCREENSHOT_NEEDED: BROWSER
URL: https://github.com/username/repo/compare
Description: Creating a pull request comparing feature branch to main
Placement: After PR creation explanation -->
PR Title Examples:
[Tests] Add comprehensive search functionality tests
[Fix] Resolve flaky checkout test timeout issues
[Hotfix] Fix critical login test blocking CI/CD
PR Description Template:
## Test Changes Summary
- Added 3 new test scenarios for search functionality
- Tests cover: valid search, empty results, special characters
## Test Coverage
- Before: 45 tests
- After: 48 tests
- New coverage: Search module 85%
## Verification
- [ ] All tests pass locally
- [ ] Tests run successfully 5+ times
- [ ] No flaky behavior observed
- [ ] Follows project naming conventions
## Related Issues
Closes #123
Merging Strategies
Merge Commit Strategy
# Preserves complete history
git checkout main
git merge --no-ff feature/search-functionality-tests
graph LR
A[main] --> B[feature branch]
B --> C[commit 1]
C --> D[commit 2]
D --> E[merge back to main]
A --> E
Squash and Merge Strategy
# Combines all commits into one
git checkout main
git merge --squash feature/search-functionality-tests
git commit -m "Add search functionality tests"
Best Practice: Use squash for feature branches with many small commits to keep main history clean.
Working with Multiple Test Branches
# List all branches
git branch -a
# Switch between branches
git checkout feature/login-tests
git checkout bugfix/payment-test-fix
# Keep feature branch updated with main
git checkout feature/search-functionality-tests
git fetch origin
git rebase origin/main
# Resolve any conflicts
# After resolving, continue rebase
git rebase --continue
git push origin feature/search-functionality-tests --force-with-lease
Handling Merge Conflicts in Tests
// Conflict scenario in test file
<<<<<<< HEAD
test('should login with valid credentials', async ({ page }) => {
await page.goto('https://practiceautomatedtesting.com/login');
await page.fill('#username', 'testuser');
=======
test('should authenticate user successfully', async ({ page }) => {
await page.goto('https://practiceautomatedtesting.com/auth/login');
await page.fill('#email', 'testuser@example.com');
>>>>>>> feature/update-login-tests
// RESOLVED version
test('should authenticate user successfully', async ({ page }) => {
await page.goto('https://practiceautomatedtesting.com/login');
await page.fill('#username', 'testuser@example.com');
# After resolving conflicts manually
git add tests/login.spec.js
git commit -m "Resolve merge conflict in login tests"
Common Mistakes Section
Mistake 1: Working Directly on Main Branch
❌ Don’t do this:
git checkout main
# Making changes directly on main
git add tests/newtest.spec.js
git commit -m "Add new test"
✅ Do this instead:
git checkout -b feature/new-test-suite
# Make changes on feature branch
git add tests/newtest.spec.js
git commit -m "Add new test"
Mistake 2: Not Pulling Latest Changes Before Creating Branch
❌ Problem: Your branch starts from outdated code
# Creating branch without updating main first
git checkout -b feature/my-tests # Based on old main!
✅ Solution:
git checkout main
git pull origin main
git checkout -b feature/my-tests
Mistake 3: Committing Large Batches of Unrelated Changes
❌ Avoid:
git add .
git commit -m "Various test updates"
# 50 files changed including multiple unrelated features
✅ Better approach:
git add tests/search.spec.js
git commit -m "Add search test scenarios"
git add tests/helpers/search-helper.js
git commit -m "Add search helper utility"
Mistake 4: Force Pushing Without Coordination
❌ Dangerous:
git push origin feature/tests --force # Can overwrite others' work
✅ Safer:
git push origin feature/tests --force-with-lease # Fails if remote has changes
Debugging Branch Issues
Issue: “Can’t switch branches - uncommitted changes”
# Solution: Stash your changes
git stash save "WIP: search tests in progress"
git checkout other-branch
# Later, return and restore
git checkout feature/search-tests
git stash pop
Issue: “Merge conflict in test files”
# View conflicted files
git status
# Use merge tool
git mergetool
# Or manually edit, then:
git add tests/conflicted-file.spec.js
git commit
Issue: “Branch is behind main by many commits”
# Option 1: Rebase (rewrites history, cleaner)
git fetch origin
git rebase origin/main
# Option 2: Merge (preserves history)
git merge origin/main
Best Practices Checklist
- ✅ Always create branches from updated main
- ✅ Use descriptive branch names:
feature/
,bugfix/
,hotfix/
- ✅ Commit frequently with clear messages
- ✅ Run tests locally before pushing
- ✅ Keep branches short-lived (< 1 week)
- ✅ Delete branches after merging
- ✅ Use pull requests for code review
- ✅ Document test changes in PR descriptions
- ✅ Resolve conflicts promptly
- ✅ Keep feature branches up-to-date with main
Hands-On Practice
Branching Strategies for Test Development and Bug Fixes
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Implement feature branch workflow for new test development
- Apply hotfix branching for critical bug fixes in test automation
- Manage parallel test development using branch strategies
- Resolve merge conflicts in test automation code
- Establish naming conventions and branch policies for test repositories
💪 Hands-On Exercise
Scenario: Multi-Team Test Automation Project
You’re part of a QA team maintaining a test automation suite. Three situations require immediate attention:
- A new login feature needs test coverage
- A critical bug broke existing payment tests in production
- Another teammate is developing API tests simultaneously
Your Task: Implement appropriate branching strategies to handle all three scenarios efficiently.
Step-by-Step Instructions
Part 1: Feature Branch for New Tests (15 minutes)
-
Create a feature branch for login tests
git checkout main git pull origin main git checkout -b feature/login-page-tests
-
Create a new test file:
tests/test_login.py
import pytest from pages.login_page import LoginPage class TestLogin: def test_valid_login(self, driver): # TODO: Implement valid login test pass def test_invalid_credentials(self, driver): # TODO: Implement invalid login test pass
-
Commit and push your changes
git add tests/test_login.py git commit -m "feat: add login page test cases" git push origin feature/login-page-tests
Part 2: Hotfix Branch for Critical Bug (10 minutes)
-
Switch to main and create hotfix branch
git checkout main git checkout -b hotfix/payment-test-selector-fix
-
Fix the broken test in
tests/test_payment.py
# BEFORE (broken selector) def test_payment_submission(self, driver): submit_btn = driver.find_element(By.ID, "submit-payment") # AFTER (fixed selector) def test_payment_submission(self, driver): submit_btn = driver.find_element(By.ID, "submit-payment-btn")
-
Commit and prepare for immediate merge
git add tests/test_payment.py git commit -m "fix: correct payment button selector" git push origin hotfix/payment-test-selector-fix
Part 3: Handle Parallel Development (15 minutes)
-
Simulate teammate’s work - create API test branch
git checkout main git checkout -b feature/api-endpoint-tests
-
Add config file that both branches might modify:
config/test_config.py
TEST_CONFIG = { "base_url": "https://test.example.com", "timeout": 30, "api_endpoints": { "users": "/api/v1/users" } }
-
Commit API test changes
git add config/test_config.py git commit -m "feat: add API test configuration" git push origin feature/api-endpoint-tests
-
Switch back to login branch and modify same config
git checkout feature/login-page-tests
Add to
config/test_config.py
:TEST_CONFIG = { "base_url": "https://test.example.com", "timeout": 30, "login_url": "/login" # Your addition }
-
Create a merge conflict scenario and resolve it
git add config/test_config.py git commit -m "feat: add login configuration" # Merge main (which now has API changes) git fetch origin git merge origin/feature/api-endpoint-tests # Resolve conflict by combining both configurations
Part 4: Implement Branch Naming Convention (5 minutes)
Create a BRANCHING_POLICY.md
file:
# Test Automation Branching Policy
## Branch Naming Convention
- Feature tests: `feature/[test-area]-tests`
- Bug fixes: `fix/[bug-description]`
- Hotfixes: `hotfix/[critical-issue]`
- Refactoring: `refactor/[area-refactored]`
## Branch Lifecycle
1. Features: Branch from `main` → PR review → Merge to `main`
2. Hotfixes: Branch from `main` → Fast-track review → Merge to `main` + `develop`
3. Delete branches after successful merge
Expected Outcomes
After completing this exercise, you should have:
✅ Three separate branches with clear purposes and naming ✅ Understanding of when to use feature vs. hotfix branches ✅ Experience resolving merge conflicts in test code ✅ A documented branching policy for your team ✅ Proper commit messages following conventions
Solution Approach
Conflict Resolution Example:
# RESOLVED config/test_config.py
TEST_CONFIG = {
"base_url": "https://test.example.com",
"timeout": 30,
"login_url": "/login", # From feature/login-page-tests
"api_endpoints": { # From feature/api-endpoint-tests
"users": "/api/v1/users"
}
}
Merge Order:
- Hotfix branch → Main (highest priority, critical fix)
- Feature/API tests → Main (completed first)
- Feature/Login tests → Main (resolve conflicts, then merge)
🎓 Key Takeaways
- Feature branches isolate test development - Keep new test suites separate until ready, enabling parallel work without interference
- Hotfix branches expedite critical fixes - Bypass normal development workflow to quickly patch production test failures
- Naming conventions prevent chaos - Clear branch names (
feature/
,hotfix/
,fix/
) communicate intent and urgency to the entire team - Merge conflicts are inevitable in collaborative testing - Configuration files and shared utilities commonly conflict; resolve by understanding both changes
- Branch policies enforce quality - Document when to branch, naming standards, and merge requirements to maintain clean test repository history
🚀 Next Steps
Practice These Skills
- Set up branch protection rules in your repository (require PR reviews, status checks)
- Practice rebasing instead of merging to maintain linear history
- Implement pre-commit hooks that run linters on test code before commits
- Create a visual branching diagram for your team using GitKraken or similar tools
Related Topics to Explore
- CI/CD Integration with Branches - Run different test suites based on branch type
- Git Flow vs. GitHub Flow - Compare full branching models for test automation
- Trunk-Based Development - Alternative approach with feature flags for tests
- Semantic Versioning for Test Suites - Tag releases of your test automation framework
- Monorepo Strategies - Managing multiple test projects in a single repository
Recommended Reading
- “Git workflows for test automation teams” - Best practices guide
- Documentation on pull request templates for test reviews
- Advanced Git commands:
cherry-pick
,rebase -i
,reflog
for test code recovery