Module 8: Fixing Mistakes and Resolving Merge Conflicts

Learn to handle the inevitable: mistakes and conflicts in test code. Practice undoing commits, recovering deleted test files, and resolving merge conflicts in test scripts, configuration files, and test data. Build confidence in recovering from common Git problems without losing work.

Resolving Merge Conflicts in Test Automation Code

Why This Matters

Picture this: You’ve spent the morning writing comprehensive API tests for a new feature. Meanwhile, your teammate has been refactoring the same test configuration file to support multiple environments. When you try to merge your branches, Git throws a merge conflict. Your test suite won’t run until you resolve it, and you’re worried about breaking either your new tests or the refactored configuration.

This is one of the most common scenarios in collaborative test automation.

Merge conflicts are inevitable when multiple test engineers work on the same codebase. Unlike production code conflicts, test automation conflicts carry unique risks:

  • Silent test failures: A poorly resolved conflict might cause tests to pass incorrectly or skip critical scenarios
  • Lost test coverage: Accidentally overwriting test cases means bugs slip through
  • Broken CI/CD pipelines: Unresolved conflicts in configuration files halt automated builds
  • Corrupted test data: Merging incompatible test datasets can invalidate your entire test suite

When You’ll Use These Skills

You’ll encounter merge conflicts regularly when:

  • Multiple engineers modify the same test files during parallel feature development
  • Test configurations diverge between different branches (staging vs. production settings)
  • Test data updates happen simultaneously across different test scenarios
  • Framework upgrades require changes to multiple test files at once
  • Refactoring efforts overlap with new test development

Common Pain Points This Lesson Addresses

  • Fear of losing work: Not knowing how to safely resolve conflicts without destroying your tests
  • Unclear conflict markers: Understanding what <<<<<<<, =======, and >>>>>>> mean in your test code
  • Configuration chaos: Merging environment-specific settings without breaking existing setups
  • Test data conflicts: Choosing which version of test data to keep when both have valid changes
  • Post-merge uncertainty: Not knowing if your tests still work correctly after resolving conflicts
  • Repeat conflicts: Dealing with the same conflicts over and over due to poor workflow practices

What You’ll Learn

This lesson takes you from conflict anxiety to conflict confidence. By the end, you’ll handle merge conflicts in test automation code as a routine part of your workflow.

Learning Path Overview

First, you’ll learn to identify and understand merge conflicts specific to test automation repositories. You’ll recognize the anatomy of conflicts in test scripts versus configuration files, and understand why Git couldn’t automatically merge them.

Next, you’ll master resolving conflicts in test script files while preserving critical test logic. You’ll practice techniques for combining test cases from different branches, ensuring no test coverage is lost in the merge.

Then, you’ll tackle conflicts in test configuration files and environment settings—often the trickiest conflicts because they involve values that must remain environment-specific. You’ll learn strategies for merging without breaking existing test environments.

Following that, you’ll handle conflicting test data files, where both versions may contain valid test scenarios. You’ll learn to merge datasets intelligently and maintain referential integrity.

Throughout the lesson, you’ll use Git conflict resolution tools and markers effectively, understanding exactly what each section means and how to leverage IDE tools to simplify the process.

Critically, you’ll learn to validate test suite functionality after resolving conflicts—the step many engineers skip that leads to broken tests reaching the main branch.

Finally, you’ll discover best practices to minimize future merge conflicts, including branching strategies, communication patterns, and file organization techniques that reduce conflict frequency.

By lesson end, you’ll confidently resolve merge conflicts while maintaining test suite integrity, turning a common source of stress into a manageable skill.


Core Content

Core Content: Resolving Merge Conflicts in Test Automation Code

Core Concepts Explained

Understanding Merge Conflicts in Test Automation

Merge conflicts occur when Git cannot automatically reconcile differences between two branches. In test automation projects, conflicts commonly arise in:

  • Test files when multiple team members modify the same test cases
  • Page Object Models when UI element locators change
  • Configuration files when different environments are updated simultaneously
  • Test data files when test datasets are modified in parallel

The Anatomy of a Merge Conflict

When a conflict occurs, Git marks the conflicting sections in your files:

<<<<<<< HEAD (Current Change)
describe('Login Test', () => {
  it('should login with valid credentials', () => {
    cy.visit('https://practiceautomatedtesting.com/login');
    cy.get('#username').type('testuser@example.com');
    cy.get('#password').type('SecurePass123!');
=======
describe('User Authentication', () => {
  it('validates successful login', () => {
    cy.visit('https://practiceautomatedtesting.com/login');
    cy.get('[data-testid="username"]').type('testuser@example.com');
    cy.get('[data-testid="password"]').type('SecurePass123!');
>>>>>>> feature/update-selectors (Incoming Change)
    cy.get('#login-button').click();
  });
});

Conflict Markers Explained:

  • <<<<<<< HEAD - Your current branch changes (what you have)
  • ======= - Divider between changes
  • >>>>>>> branch-name - Incoming changes from the branch being merged

Conflict Resolution Workflow

graph TD
    A[Start Merge/Pull] --> B{Conflicts Detected?}
    B -->|No| C[Merge Complete]
    B -->|Yes| D[Identify Conflicting Files]
    D --> E[Open Conflicted Files]
    E --> F[Review Both Changes]
    F --> G[Edit to Resolve]
    G --> H[Remove Conflict Markers]
    H --> I[Test Resolution]
    I --> J[Stage Resolved Files]
    J --> K[Commit Merge]
    K --> C

Step-by-Step Conflict Resolution Process

Step 1: Detect and Identify Conflicts

When you attempt to merge or pull changes:

$ git merge feature/update-tests
Auto-merging tests/login.spec.js
CONFLICT (content): Merge conflict in tests/login.spec.js
Auto-merging pages/loginPage.js
CONFLICT (content): Merge conflict in pages/loginPage.js
Automatic merge failed; fix conflicts and then commit the result.

Check which files have conflicts:

$ git status
On branch main
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   tests/login.spec.js
        both modified:   pages/loginPage.js

Step 2: Analyze the Conflicts

Example 1: Test Specification Conflict

// File: tests/checkout.spec.js
<<<<<<< HEAD
describe('Checkout Process', () => {
  it('should complete purchase with credit card', () => {
    cy.visit('https://practiceautomatedtesting.com/shop');
    cy.get('.product-item').first().click();
    cy.get('#add-to-cart').click();
    cy.get('#checkout-btn').click();
    
    // Fill payment details
    cy.get('#card-number').type('4111111111111111');
    cy.get('#expiry').type('12/25');
    cy.get('#cvv').type('123');
    cy.get('#submit-payment').click();
    
    cy.get('.success-message').should('be.visible');
  });
});
=======
describe('E2E Checkout Flow', () => {
  beforeEach(() => {
    cy.visit('https://practiceautomatedtesting.com/shop');
  });
  
  it('completes purchase successfully', () => {
    // Add product to cart
    cy.get('[data-testid="product-card"]').first().click();
    cy.get('[data-testid="add-to-cart"]').click();
    cy.get('[data-testid="cart-checkout"]').click();
    
    // Payment processing
    cy.fillPaymentForm({
      cardNumber: '4111111111111111',
      expiry: '12/25',
      cvv: '123'
    });
    
    cy.get('[data-testid="confirm-order"]').click();
    cy.contains('Order confirmed').should('exist');
  });
});
>>>>>>> feature/refactor-checkout-tests

Analysis:

  • HEAD version: Uses CSS selectors, inline payment details
  • Incoming version: Uses data-testid selectors, custom command for payment, includes beforeEach hook

Step 3: Choose Resolution Strategy

Strategy Options:

  1. Keep Current (HEAD) - When your changes are correct
  2. Accept Incoming - When the other branch has better code
  3. Combine Both - Merge the best of both approaches
  4. Rewrite - Create a new solution using elements from both

Recommended Resolution (Combining Both):

// File: tests/checkout.spec.js
describe('E2E Checkout Flow', () => {
  beforeEach(() => {
    cy.visit('https://practiceautomatedtesting.com/shop');
  });
  
  it('should complete purchase with credit card', () => {
    // Add product to cart - using better data-testid selectors
    cy.get('[data-testid="product-card"]').first().click();
    cy.get('[data-testid="add-to-cart"]').click();
    cy.get('[data-testid="cart-checkout"]').click();
    
    // Fill payment details - using custom command for reusability
    cy.fillPaymentForm({
      cardNumber: '4111111111111111',
      expiry: '12/25',
      cvv: '123'
    });
    
    cy.get('[data-testid="confirm-order"]').click();
    
    // Clear assertion combining both approaches
    cy.get('.success-message').should('be.visible');
    cy.contains('Order confirmed').should('exist');
  });
});

Step 4: Resolve Page Object Conflicts

Example 2: Page Object Model Conflict

// File: pages/loginPage.js
<<<<<<< HEAD
class LoginPage {
  constructor() {
    this.usernameInput = '#username';
    this.passwordInput = '#password';
    this.loginButton = '#login-button';
  }
  
  login(username, password) {
    cy.get(this.usernameInput).type(username);
    cy.get(this.passwordInput).type(password);
    cy.get(this.loginButton).click();
  }
}
=======
class LoginPage {
  get usernameInput() { return cy.get('[data-testid="username"]'); }
  get passwordInput() { return cy.get('[data-testid="password"]'); }
  get loginButton() { return cy.get('[data-testid="login-btn"]'); }
  get errorMessage() { return cy.get('.error-alert'); }
  
  login(username, password) {
    this.usernameInput.type(username);
    this.passwordInput.type(password);
    this.loginButton.click();
  }
  
  verifyLoginError(expectedMessage) {
    this.errorMessage.should('contain', expectedMessage);
  }
}
>>>>>>> feature/improve-page-objects

Best Resolution (Accept incoming with improvements):

// File: pages/loginPage.js
class LoginPage {
  // Using getters for dynamic element selection
  get usernameInput() { return cy.get('[data-testid="username"]'); }
  get passwordInput() { return cy.get('[data-testid="password"]'); }
  get loginButton() { return cy.get('[data-testid="login-btn"]'); }
  get errorMessage() { return cy.get('.error-alert'); }
  get successMessage() { return cy.get('.success-alert'); }
  
  visit() {
    cy.visit('https://practiceautomatedtesting.com/login');
    return this;
  }
  
  login(username, password) {
    this.usernameInput.type(username);
    this.passwordInput.type(password);
    this.loginButton.click();
    return this;
  }
  
  verifyLoginError(expectedMessage) {
    this.errorMessage.should('contain', expectedMessage);
    return this;
  }
  
  verifyLoginSuccess() {
    this.successMessage.should('be.visible');
    return this;
  }
}

export default new LoginPage();

Step 5: Handle Configuration Conflicts

Example 3: Configuration File Conflict

// File: cypress.config.js
<<<<<<< HEAD
module.exports = {
  e2e: {
    baseUrl: 'https://practiceautomatedtesting.com',
    viewportWidth: 1280,
    viewportHeight: 720,
    video: true,
    screenshotOnRunFailure: true
  }
}
=======
module.exports = {
  e2e: {
    baseUrl: 'https://practiceautomatedtesting.com',
    viewportWidth: 1920,
    viewportHeight: 1080,
    retries: {
      runMode: 2,
      openMode: 0
    },
    defaultCommandTimeout: 10000
  }
}
>>>>>>> feature/update-config

Resolution (Combine all useful settings):

// File: cypress.config.js
module.exports = {
  e2e: {
    baseUrl: 'https://practiceautomatedtesting.com',
    // Use larger viewport from feature branch
    viewportWidth: 1920,
    viewportHeight: 1080,
    // Keep video settings from HEAD
    video: true,
    screenshotOnRunFailure: true,
    // Add retry logic from feature branch
    retries: {
      runMode: 2,
      openMode: 0
    },
    // Add timeout from feature branch
    defaultCommandTimeout: 10000
  }
}

Step 6: Stage and Commit Resolved Files

After resolving all conflicts:

# Run tests to verify resolution
$ npm test

# Stage the resolved files
$ git add tests/checkout.spec.js
$ git add pages/loginPage.js
$ git add cypress.config.js

# Verify all conflicts are resolved
$ git status
On branch main
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:
        modified:   tests/checkout.spec.js
        modified:   pages/loginPage.js
        modified:   cypress.config.js

# Commit the merge
$ git commit -m "Merge feature branches: resolved conflicts in checkout tests, login page object, and config

- Combined data-testid selectors with existing test structure
- Improved page object with getter pattern and method chaining
- Merged configuration settings from both branches
- All tests passing"

Practical Code Examples

Complete Conflict Resolution Workflow

# Step 1: Attempt merge
$ git checkout main
$ git merge feature/update-selectors

# Step 2: Conflicts detected - check status
$ git status

# Step 3: Open IDE and review conflicts
# (Resolve conflicts in each file as shown above)

# Step 4: Test your resolution
$ npm run test:local

# Output:
#   Checkout Flow Tests
#     ✓ should complete purchase with credit card (2543ms)
#   Login Tests
#     ✓ should login with valid credentials (1234ms)
#     ✓ should show error for invalid credentials (987ms)
#
#   3 passing (5s)

# Step 5: Stage resolved files
$ git add .

# Step 6: Complete the merge
$ git commit -m "Resolved merge conflicts in test suite"

# Step 7: Push to remote
$ git push origin main

Using Git Tools for Conflict Resolution

Option 1: VS Code Built-in Merge Editor

<!-- SCREENSHOT_NEEDED: IDE
     IDE: VS Code
     View: 3-way merge editor
     Description: Showing Current, Incoming, and Result panels
     Placement: in this section -->

Option 2: Command Line Resolution

# Accept all current changes
$ git checkout --ours tests/login.spec.js

# Accept all incoming changes
$ git checkout --theirs pages/loginPage.js

# Then stage the files
$ git add tests/login.spec.js pages/loginPage.js

Option 3: Using Merge Tool

# Configure merge tool (one-time setup)
$ git config --global merge.tool vscode
$ git config --global mergetool.vscode.cmd 'code --wait $MERGED'

# Launch merge tool for conflicts
$ git mergetool

Common Mistakes Section

Mistake 1: Leaving Conflict Markers in Code

Wrong:

describe('Login Test', () => {
<<<<<<< HEAD
  it('should login successfully', () => {
=======
  it('validates user login', () => {
>>>>>>> feature/update-tests
    cy.visit('https://practiceautomatedtesting.com/login');
  });
});

Impact: Code won’t run, tests will fail with syntax errors.

Fix: Always remove ALL conflict markers (<<<<<<<, =======, >>>>>>>) before committing.

Mistake 2: Not Testing After Resolution

Wrong Approach:

# Resolve conflict
$ vim tests/checkout.spec.js
$ git add tests/checkout.spec.js
$ git commit -m "Fixed conflict"
$ git push
# Tests fail in CI/CD pipeline!

Correct Approach:

# Resolve conflict
$ vim tests/checkout.spec.js
# TEST FIRST!
$ npm test
# Verify all tests pass
$ git add tests/checkout.spec.js
$ git commit -m "Resolved checkout test conflicts - all tests passing"
$ git push

Mistake 3: Blindly Accepting One Side

Problem: Always choosing “Accept Current” or “Accept Incoming” without analysis.

Example:

// Accepting incoming might lose important test coverage
// Current has: login validation, error handling, success verification
// Incoming has: only login validation

// Don't blindly accept - combine the best parts!

Mistake 4: Forgetting to Stage All Resolved Files

Debugging:

$ git commit
# Error: Committing is not possible because you have unmerged files.

$ git status
Unmerged paths:
  both modified:   tests/api.spec.js

# Solution: Stage the forgotten file
$ git add tests/api.spec.js
$ git commit -m "Resolved all conflicts"

Mistake 5: Conflicting Test Data

Problem:

// Both branches modify the same test data
// Current: uses email testuser@example.com
// Incoming: uses email newuser@example.com
// Resolution uses: testuser@example.com
// But incoming branch created user with newuser@example.com!

Solution:

// Ensure test data consistency
describe('User Registration', () => {
  const testUser = {
    email: 'testuser@example.com',
    password: 'SecurePass123!'
  };
  
  before(() => {
    // Clean up and create fresh test user



---

## Hands-On Practice

# EXERCISE

## Hands-On Exercise: Resolving Test Automation Merge Conflicts

### Scenario
You're working on a test automation project with your team. Two developers have been working on the same test file in separate branches. Now it's time to merge, and conflicts have emerged in the test suite.

### Task
Resolve merge conflicts in a Selenium test suite where multiple team members have modified the same test files, then ensure all tests pass successfully.

### Step-by-Step Instructions

#### Setup (Simulating the Conflict)

1. **Clone or create a new repository:**
```bash
mkdir test-merge-practice
cd test-merge-practice
git init
  1. Create the initial test file on main branch:

Starter Code - test_login.py:

import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By

class TestLogin:
    def setup_method(self):
        self.driver = webdriver.Chrome()
        self.driver.get("https://example-app.com/login")
    
    def test_successful_login(self):
        self.driver.find_element(By.ID, "username").send_keys("testuser")
        self.driver.find_element(By.ID, "password").send_keys("password123")
        self.driver.find_element(By.ID, "login-button").click()
        assert "Dashboard" in self.driver.title
    
    def teardown_method(self):
        self.driver.quit()
  1. Commit the initial file:
git add test_login.py
git commit -m "Initial login test"
  1. Create and switch to feature branch A:
git checkout -b feature/add-validation
  1. Modify test_login.py (Developer A’s changes):
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class TestLogin:
    def setup_method(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(10)
        self.driver.get("https://example-app.com/login")
    
    def test_successful_login(self):
        self.driver.find_element(By.ID, "username").send_keys("testuser")
        self.driver.find_element(By.ID, "password").send_keys("password123")
        self.driver.find_element(By.ID, "login-button").click()
        
        # Added explicit wait
        WebDriverWait(self.driver, 10).until(
            EC.title_contains("Dashboard")
        )
        assert "Dashboard" in self.driver.title
    
    def test_invalid_credentials(self):
        self.driver.find_element(By.ID, "username").send_keys("wronguser")
        self.driver.find_element(By.ID, "password").send_keys("wrongpass")
        self.driver.find_element(By.ID, "login-button").click()
        
        error_msg = self.driver.find_element(By.CLASS_NAME, "error-message")
        assert "Invalid credentials" in error_msg.text
    
    def teardown_method(self):
        self.driver.quit()
  1. Commit Developer A’s changes:
git add test_login.py
git commit -m "Add validation test and explicit waits"
  1. Switch back to main and create feature branch B:
git checkout main
git checkout -b feature/add-logout
  1. Modify test_login.py (Developer B’s changes):
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By

class TestLogin:
    @pytest.fixture(autouse=True)
    def setup_method(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get("https://example-app.com/login")
        yield
        self.driver.quit()
    
    def test_successful_login(self):
        self.driver.find_element(By.ID, "username").send_keys("testuser")
        self.driver.find_element(By.ID, "password").send_keys("password123")
        self.driver.find_element(By.ID, "login-button").click()
        assert "Dashboard" in self.driver.title
    
    def test_logout_functionality(self):
        # Login first
        self.driver.find_element(By.ID, "username").send_keys("testuser")
        self.driver.find_element(By.ID, "password").send_keys("password123")
        self.driver.find_element(By.ID, "login-button").click()
        
        # Then logout
        self.driver.find_element(By.ID, "logout-button").click()
        assert "Login" in self.driver.title
  1. Commit Developer B’s changes:
git add test_login.py
git commit -m "Add logout test and use pytest fixtures"

Your Task: Resolve the Conflicts

  1. Merge feature/add-validation into main:
git checkout main
git merge feature/add-validation
  1. Now merge feature/add-logout (this will create conflicts):
git merge feature/add-logout
  1. Resolve the conflicts by:

    • Opening test_login.py in your editor
    • Combining the best elements from both branches:
      • Keep pytest fixtures approach (Developer B)
      • Keep explicit waits (Developer A)
      • Include ALL three test methods
      • Remove conflict markers (<<<<<<<, =======, >>>>>>>)
    • Ensuring consistent coding style
    • Making sure imports are complete and not duplicated
  2. After resolving, verify your solution:

git add test_login.py
git commit -m "Merge feature/add-logout with resolved conflicts"
  1. Run the tests to ensure nothing broke:
pytest test_login.py -v

Expected Outcome

Resolved test_login.py:

import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class TestLogin:
    @pytest.fixture(autouse=True)
    def setup_method(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.implicitly_wait(10)
        self.driver.get("https://example-app.com/login")
        yield
        self.driver.quit()
    
    def test_successful_login(self):
        self.driver.find_element(By.ID, "username").send_keys("testuser")
        self.driver.find_element(By.ID, "password").send_keys("password123")
        self.driver.find_element(By.ID, "login-button").click()
        
        WebDriverWait(self.driver, 10).until(
            EC.title_contains("Dashboard")
        )
        assert "Dashboard" in self.driver.title
    
    def test_invalid_credentials(self):
        self.driver.find_element(By.ID, "username").send_keys("wronguser")
        self.driver.find_element(By.ID, "password").send_keys("wrongpass")
        self.driver.find_element(By.ID, "login-button").click()
        
        error_msg = self.driver.find_element(By.CLASS_NAME, "error-message")
        assert "Invalid credentials" in error_msg.text
    
    def test_logout_functionality(self):
        self.driver.find_element(By.ID, "username").send_keys("testuser")
        self.driver.find_element(By.ID, "password").send_keys("password123")
        self.driver.find_element(By.ID, "login-button").click()
        
        WebDriverWait(self.driver, 10).until(
            EC.title_contains("Dashboard")
        )
        
        self.driver.find_element(By.ID, "logout-button").click()
        assert "Login" in self.driver.title

Solution Approach

  1. Identify conflict sections marked by Git
  2. Analyze both versions to understand what each developer was trying to achieve
  3. Merge logically by:
    • Taking the better structural approach (pytest fixtures)
    • Preserving all new test methods from both branches
    • Combining improvements (explicit waits + window maximize)
    • Ensuring no duplicate imports
  4. Test thoroughly to verify the merged code works
  5. Commit with a descriptive message explaining the resolution

KEY TAKEAWAYS

What You’ve Learned

Conflict Resolution Process: Merge conflicts occur when multiple developers modify the same lines of code. Understanding Git conflict markers (<<<<<<<, =======, >>>>>>>) is essential for identifying and resolving these issues.

Evaluating Changes Contextually: Not all conflicts should be resolved by simply “accepting yours” or “accepting theirs.” The best resolution often involves combining improvements from both branches while maintaining code consistency and functionality.

Test-Specific Considerations: When resolving conflicts in test automation code, prioritize preserving test coverage, maintaining consistent wait strategies, and ensuring fixture/setup configurations work for all test methods.

Post-Merge Verification: Always run your test suite after resolving conflicts to ensure the merged code is functional. Merge conflicts can inadvertently break test logic, remove necessary imports, or create syntax errors.

Communication Matters: Many merge conflicts can be prevented or simplified through team communication about who’s working on which files, establishing coding standards, and reviewing changes regularly through pull requests.

When to Apply These Skills

  • Daily team collaboration when multiple testers work on the same test suites
  • Feature branch merges before deploying test automation updates
  • Code reviews to catch potential conflicts early
  • CI/CD pipeline failures caused by merge issues
  • Test suite refactoring projects involving multiple contributors

NEXT STEPS

What to Practice

  1. Create intentional conflicts in your own projects to practice resolution without pressure
  2. Use different merge tools (VS Code, IntelliJ, GitKraken) to find your preferred workflow
  3. Practice 3-way merges understanding base, theirs, and yours perspectives
  4. Resolve conflicts in different file types: configuration files (pytest.ini, conftest.py), page objects, and test data files
  • Git Rebase vs Merge: Learn when rebasing is better than merging for cleaner history
  • Branch Strategies: Explore GitFlow, trunk-based development, and their impact on conflicts
  • Code Review Best Practices: Prevent conflicts through better collaboration workflows
  • Continuous Integration: Set up automated testing to catch integration issues early
  • Conflict Prevention: Modular test design, page object patterns, and shared utility functions that reduce overlapping work
  • Advanced Git Commands: git mergetool, git diff, git log --merge for better conflict analysis
  • Week 1: Practice resolving simple conflicts daily (15 minutes)
  • Week 2: Work with a partner creating and resolving realistic conflicts
  • Week 3: Apply conflict resolution in your actual team projects
  • Ongoing: Review team merge conflicts as learning opportunities

Pro Tip: Keep a personal “conflict resolution cheat sheet” with your team’s specific patterns and decisions for common conflict scenarios. This becomes invaluable for maintaining consistency across your test automation codebase.