Resource library

QA How-To

Resolving merge conflicts as a tester (2026)

Learn resolving merge conflicts as a tester: Git markers, page objects, lockfiles, CI workflows, verification checklists, and interview-ready answers for 2026.

18 min read | 2,893 words

TL;DR

Resolving merge conflicts as a tester means combining both sides intentionally, regenerating lockfiles and snapshots, removing all markers, and verifying with focused tests. Never keep one side blindly for shared page objects or CI workflows.

Key Takeaways

  • Integrate main into your test branch daily so conflicts stay small.
  • Remove every conflict marker and re-read the resulting file as product behavior.
  • Prefer manual combination for page objects, fixtures, and shared helpers.
  • Regenerate lockfiles and visual baselines instead of hand-merging them.
  • Run focused tests, typecheck, and a marker search before you push.
  • Use --ours/--theirs only when one side fully supersedes the file.
  • Treat unclear UI conflicts as collaboration problems, not pure Git problems.

Resolving merge conflicts as a tester is a core CI and collaboration skill in 2026, not a side chore reserved for developers. When your Playwright suite, page objects, fixtures, and test data all live in the same repository as product code, you will open pull requests that touch the same files as application engineers. The practical goal is simple: keep tests green, keep history clean, and never resolve a conflict by guessing.

This guide walks through how QA and SDET engineers detect conflicts early, resolve text and structural conflicts with Git, protect generated and binary artifacts, and verify the suite after a merge. You will get runnable commands, a comparison of strategies, a checklist for test-specific conflict patterns, and interview answers you can reuse. Treat every resolution as a mini verification task: the code that remains must still express the product behavior you intend to assert.

TL;DR

Situation Prefer
Conflict in a page object or selector helper Open both sides, re-open the app or DOM, re-derive the locator
Conflict only in import order or formatting Re-run the formatter after keeping the correct logic
Conflict in generated reports or screenshots Do not merge binaries by hand; regenerate after the code merge
Conflict in package-lock or similar lockfile Prefer regenerating the lockfile after resolving package.json
Large, overlapping feature branches Rebase or merge main into your branch daily
Unsure which side is correct Stop, run the related tests, and ask the other author

If you remember only one rule, remember this: Git can combine text, but only you can combine intent.

1. Why Testers Own Merge Conflicts in Modern Repos

Monorepos and full-stack feature branches mean automated tests share the same Git history as production code. A tester who only ever works on a pure tests/ repo still collides with other QA authors on shared utilities, CI workflows, and fixtures. When the product team moves a button, two branches often update the same locator file.

Owning the conflict is part of owning the suite. If you leave a half-resolved page object, the next pipeline failure will look like a flaky test. Interviewers and tech leads notice whether you can explain both the Git mechanics and the product risk. In many teams the SDET is the last person to touch a release branch before a hotfix lands, which means you may resolve conflicts under time pressure with incomplete context.

Internal practices that reduce conflict volume include small pull requests, frequent integration with the default branch, and clear ownership of shared helpers. For broader CI placement of your suite, see how to add CI to a test framework. Pair that with a DevOps learning path such as the DevOps for QA roadmap so merge skills sit next to pipeline design, not in isolation.

Modern code review culture also raises the bar. Reviewers expect conflict resolutions to preserve commit message quality, avoid drive-by refactors, and leave the diff reviewable. A resolution that "makes it compile" but silently drops a negative path assertion is a product defect waiting for production.

2. Detect Conflicts Before They Block a Pull Request

Do not wait for GitHub to paint a red "This branch has conflicts" banner. Integrate early and on a schedule that matches your sprint cadence:

git fetch origin
git status
git switch feature/qa-checkout-flow
git merge origin/main
# or, if your team prefers a linear history:
# git rebase origin/main

If Git reports conflicts, the working tree marks them. Useful inspection commands:

git status
git diff --name-only --diff-filter=U
git diff
git log --oneline --left-right HEAD...MERGE_HEAD | head -n 40

Unmerged paths appear in git status as both modified. Open those files only after you understand whether the conflict is textual, rename-related, or a delete/modify collision. The left-right log helps you see which commits from each side are participating, which is invaluable when several authors touched the same module.

CI can also surface conflicts when a protected branch requires a successful merge commit. That is a late signal. Local daily integration keeps conflict chunks small enough to reason about. Some teams add a scheduled job that opens a draft PR from main into long-lived release branches solely to detect drift; you can adapt the same idea for a long-running automation branch.

If your remote host blocks force pushes on the default branch, that protection does not save you from a broken resolution on a feature branch. Detection is local discipline plus green CI after the push.

3. Read Conflict Markers Correctly

A standard text conflict looks like this:

<<<<<<< HEAD
await page.getByRole('button', { name: 'Pay now' }).click();
=======
await page.getByTestId('checkout-submit').click();
>>>>>>> origin/main

Meaning:

  1. <<<<<<< HEAD starts your current branch version.
  2. ======= separates the two sides.
  3. >>>>>>> <ref> ends the incoming side.

You must remove all three marker lines. Leaving a marker is a syntax error and a serious review failure. After editing, the file should contain one coherent implementation, not a collage of both sides.

For a three-way merge, Git also knows the common ancestor. Tools such as git mergetool or an editor with a three-pane merge view show base, ours, and theirs. Use the base when both sides edited the same method for different reasons. Example: the base had a brittle CSS selector; your branch switched to getByRole; main switched to getByTestId. The base tells you both sides were fixing the same pain, so you pick one strategy and delete the rest.

Nested conflicts can appear if someone committed markers by mistake earlier. Search the repository for marker patterns before you declare victory:

# ripgrep example; use your preferred search
rg -n '^(<<<<<<<|=======|>>>>>>>)' .

If you use Visual Studio Code, JetBrains IDEs, or GitHub Desktop, accept each hunk deliberately. Clicking "Accept both" without reading is a common source of duplicated function definitions and broken imports.

4. Choose Ours, Theirs, or a Manual Combination

Git can take one side wholesale for a file:

# keep the version from the branch you are on
git checkout --ours path/to/file.ts

# keep the version from the branch being merged in
git checkout --theirs path/to/file.ts

git add path/to/file.ts

These shortcuts are safe only when you truly want one complete file. In test code, that is rarely true for page objects: one branch may fix a flaky wait while the other updates a selector. Prefer a manual combination and then re-run the affected tests.

Strategy When it is safe Risk
Keep ours Incoming change is unrelated noise in that file Lose a real product fix from main
Keep theirs Your branch only had temporary or obsolete edits Lose intentional test coverage
Manual merge Both sides changed behavior or shared helpers Higher effort, lowest defect risk
Regenerate Lockfiles, snapshots, compiled reports Wrong if package.json still conflicts

Never use a blanket git checkout --theirs . across the repository. During a rebase, remember that ours and theirs swap relative meaning compared to a normal merge. Always read git status labels for the operation you are in rather than memorizing one mapping forever.

When a whole directory should come from one side (for example, a generated API client you never edit by hand), path-level checkout is acceptable if the team documents that policy. For human-maintained tests, path-level checkout is a last resort.

5. Resolve Common Test-Code Conflict Patterns

Page objects and locators

Two authors often rename the same control. Resolve by checking the live UI or a current design token, not by string length. Prefer stable strategies such as roles and test ids when the product team supports them. After the merge, run the single spec that exercises that screen.

Shared fixtures and hooks

Fixture conflicts frequently involve setup order, authentication helpers, or browser context options. Keep a single authentication path when possible. Duplicate login helpers are a common source of post-merge flakes. If both sides add a storageState path, confirm the file is produced in CI and gitignored correctly.

Assertions and test data

If one branch strengthens an assertion and another changes fixture data, update both so the assertion still matches intentional product output. Do not weaken assertions only to make the merge compile. Watch for timezone, locale, and currency formatting conflicts when data factories differ between branches.

CI workflow YAML

GitHub Actions files conflict when two branches add jobs or change matrix axes. YAML indentation errors after a bad merge fail the entire pipeline. Validate with actionlint or a dry run when your platform supports it. Reusable patterns are covered in reusable GitHub Actions workflows for QA.

Example of a careful manual resolution mindset for a Playwright locator file:

import { type Page, expect } from '@playwright/test';

export class CheckoutPage {
  constructor(private readonly page: Page) {}

  // Prefer one stable strategy after reviewing both branches and the UI.
  submitOrder() {
    return this.page.getByTestId('checkout-submit').click();
  }

  async expectConfirmation() {
    await expect(this.page.getByRole('heading', { name: 'Order confirmed' })).toBeVisible();
  }
}

Parallel suite config

Conflicts in playwright.config.ts, TestNG XML, or pytest markers often encode capacity decisions: workers, retries, baseURL, and projects. After merging, confirm that local defaults still work offline and that CI overrides remain in environment-specific config. A silent change from fullyParallel: true to false can double pipeline time without failing a single assertion.

6. Handle Lockfiles, Snapshots, and Binary Artifacts

Text merge markers do not work well for lockfiles or binary screenshots. Practical rules:

  1. Resolve package.json (or pom.xml, requirements inputs) first.
  2. Delete the conflicted lockfile if needed, reinstall, and commit the regenerated lockfile.
  3. For visual snapshots, accept neither binary blindly. Re-run the visual suite on a known-good environment and commit fresh baselines only when the UI change is intentional.
  4. Never commit Allure or HTML report output that collided in a branch. Reports belong in CI artifacts, not long-lived source paths.
# Example after resolving package.json conflicts in a Node test monorepo
rm package-lock.json
npm install
git add package.json package-lock.json

For Python poetry or pip-tools flows, regenerate the lock with the team standard command rather than editing hash lines by hand. For Maven, prefer regenerating or re-resolving dependency trees through the build tool after pom.xml is clean.

Binary merge drivers exist in Git, but most QA teams are better served by process: do not store large videos or HTML reports in Git; keep baselines small and reviewed; document who approves visual updates.

7. Finish the Merge: Stage, Test, Commit

After every conflicted path is fixed:

git add path/to/resolved-file.ts
git status
# optional focused run before a full suite
npx playwright test tests/checkout.spec.ts
git commit   # completes the merge commit if you used git merge
# if you were rebasing:
# git rebase --continue

If you are mid-rebase and need to abort:

git rebase --abort

If you are mid-merge and need to abort:

git merge --abort

Verification checklist before you push:

  1. No conflict markers remain (rg '<<<<<<<|=======|>>>>>>>' . or your editor search).
  2. Project builds or typechecks.
  3. Unit and smoke tests for touched areas pass.
  4. CI config parses.
  5. No accidental debug .only or skipped critical tests introduced during the scramble.
  6. Secrets and .env samples still do not contain real credentials (see secrets management in CI for tests).
  7. Diff size is explainable in the PR description.

Write merge commit messages that mention the conflict areas when the resolution was non-trivial. Future you will thank present you when bisecting a regression introduced during a messy integration.

8. Prevent Conflicts With Branch Hygiene

Practice Effect on conflicts
Pull or rebase main daily Smaller, fresher diffs
Keep PRs under a few hundred lines when possible Easier reviews and merges
Split shared helper refactors into their own PR Reduces dual edits on utilities
Agree on formatter and import sort rules Avoids pure style conflicts
Avoid long-lived feature branches for tests only Less drift from product code
Communicate when rewriting a core page object Others pause or stack their work
Use CODEOWNERS for critical test utilities Routes reviews to people who know the design

Teams that treat test code as second-class still create large late conflicts. Elevating suite ownership in planning reduces emergency merges on release day. If two squads must rewrite the same checkout helper, schedule a short design huddle before both implement divergent versions.

Feature flags can reduce product conflicts but do not eliminate test conflicts. You may still need dual assertions while a flag rolls out. Document the flag name in the test title so future merges know which behavior is transitional.

9. Collaborate When You Cannot Decide Alone

Some conflicts encode product decisions: which error message is correct, which feature flag default wins, whether a field is required. Git cannot answer those. Open a short thread on the pull request, link the conflicted hunk, and propose a resolution with a screenshot or log when UI behavior is involved.

When the other author is unavailable, prefer the default branch behavior for user-facing flows, keep your new coverage behind a clear test name, and follow up. Document temporary skips with a ticket id; never leave silent gaps. A skip without a ticket is how coverage disappears for months.

Pairing on a difficult conflict is often faster than trading comments. Fifteen minutes on a call with the other author, screen shared on the three-way merge view, prevents a day of broken pipelines.

10. Map Git Concepts to Interview-Ready Language

Be ready to explain:

  • Working tree, index (staging area), and commit graph.
  • Fast-forward versus merge commit.
  • Rebase as replaying commits onto a new base.
  • ours and theirs during merge versus during rebase (they swap relative meaning; always check git status labels).
  • Why force-pushing a rewritten public branch is dangerous.
  • The difference between resolving in the working tree and recording the resolution with git add.
  • How CI protected branches interact with required status checks after a merge resolution.

A strong tester answer connects these mechanics to risk: wrong locator kept, lockfile corrupted, secret committed while resolving, or tests skipped to force a green merge. Weak answers stop at "I click accept incoming in the IDE."

Practice explaining a real conflict from your last sprint in under two minutes. Structure: context, both changes, decision, verification. That narrative is more convincing than memorized definitions alone.

11. A Worked Mini Scenario

Imagine branch feature/qa-discount adds a Playwright check for a discount badge. Branch main renames the badge test id and extracts a shared CartPage. Your merge conflict sits in cart.page.ts and checkout.spec.ts.

Reasonable resolution steps:

  1. Keep the extracted CartPage from main as the structural winner.
  2. Port your discount assertions onto the extracted methods.
  3. Update the test id to the value currently in the running app.
  4. Run npx playwright test tests/checkout.spec.ts tests/cart.spec.ts.
  5. Commit the merge with a message that mentions cart page extraction plus discount coverage.

That sequence shows product awareness, structural respect for mainline refactors, and verification. It is the pattern interviewers hope you internalize.

Interview Questions and Answers

Q: How do you resolve a merge conflict in a page object?

I inspect both sides and the current application UI, then keep a single coherent locator strategy. I remove all conflict markers, run the related specs, and only then stage the file. If the product behavior is unclear, I check with the feature owner before merging.

Q: What is the difference between merge and rebase when updating your test branch?

git merge origin/main creates a merge commit and preserves exact history. git rebase origin/main replays your commits on top of main for a linear history. I follow team policy; during either operation I resolve conflicts commit by commit and re-test.

Q: When would you use --ours or --theirs?

Only when one side fully supersedes the other for that file, such as discarding a temporary experiment. Shared test helpers almost always need a manual combination so no assertion or setup step is lost.

Q: How do you handle package-lock conflicts?

I resolve package.json first, regenerate the lockfile with the standard install command, run a quick test bootstrap, and commit both files together. I do not hand-edit integrity hashes.

Q: What do conflict markers look like and why must they be removed?

Git inserts <<<<<<<, =======, and >>>>>>> around conflicting regions. Leaving them breaks compilation or causes tests to execute invalid code. Reviewers treat leftover markers as a critical defect.

Q: How do you avoid large conflicts on long test branches?

I integrate main daily, keep pull requests focused, coordinate refactors of shared fixtures, and rely on consistent formatting. Early small conflicts are cheaper than one end-of-sprint mega merge.

Q: What is a delete/modify conflict?

One branch deleted a file while another modified it. I decide whether the file should still exist based on product and suite needs, then either keep a restored version with the modifications or accept the deletion and move coverage elsewhere.

Q: How do you handle conflicts in GitHub Actions workflow files?

I compare job names, triggers, secrets usage, and artifact paths from both sides, then produce one valid YAML document. I run a workflow linter when available and trigger a dry CI run on the branch before calling the resolution complete.

Common Mistakes

  • Accepting one side blindly to make Git stop complaining.
  • Leaving conflict markers in YAML or source files.
  • Hand-merging lockfiles and snapshot binaries.
  • Resolving without running any test.
  • Confusing ours/theirs during a rebase.
  • Committing debug test.only or password edits while stressed.
  • Force-pushing shared branches after a messy rebase without team agreement.
  • Skipping formatter and typecheck after a large resolution.
  • Resolving CI workflow conflicts without validating job names and artifact paths.
  • Treating conflict resolution as pure Git trivia instead of product verification.
  • Using "Accept both" in the IDE and shipping duplicated methods.
  • Storing HTML reports in Git so every run becomes a conflict generator.

Conclusion

Resolving merge conflicts as a tester means protecting the meaning of the suite, not only the syntax of the files. Integrate early, read markers carefully, combine behavioral changes deliberately, regenerate artifacts that should not be merged by hand, and verify with focused tests before you push.

Practice on a sample repository this week: create two branches that edit the same Playwright page object, merge them, and write down your verification steps. That rehearsal pays off the next time a release branch turns red, and it is one of the fastest ways to look senior in a collaborative engineering org.

Interview Questions and Answers

Explain how you resolve merge conflicts in automated tests.

I fetch and merge or rebase the default branch, list unmerged files, and open each conflict with attention to product behavior. I remove markers, combine locator and fixture changes carefully, regenerate lockfiles when needed, run focused tests, then stage and continue the merge or rebase.

What is the risk of accepting ours or theirs blindly?

You can drop critical coverage, keep a broken locator, or lose a CI security control from the other side. Blind acceptance optimizes for unblocking Git, not for protecting the suite or the release.

How do conflict resolutions differ for binary snapshots?

Binary files cannot be line-merged meaningfully. I resolve the code first, re-run the visual tests in a controlled environment, and commit regenerated baselines only when the UI change is intentional and reviewed.

What is a modify/delete conflict?

One branch deleted a file while another changed it. I decide from suite design whether the file still belongs, restore and update it if coverage remains necessary, or accept deletion and relocate tests.

How do you verify a merge before requesting review?

I ensure git status is clean of unmerged paths, search for conflict markers, run lint/typecheck, run targeted automated tests, and sanity-check workflow YAML if CI files changed.

Why should testers understand rebase abort and merge abort?

When a resolution goes wrong, abort commands restore the pre-operation state so I can restart with a clearer plan. That is safer than stacking incorrect commits to escape a messy conflict.

How do merge conflicts appear in interviews for SDET roles?

Interviewers ask about Git markers, ours/theirs, lockfiles, and how you protect test intent. They want engineers who can collaborate on shared repositories without breaking pipelines.

Frequently Asked Questions

What does resolving merge conflicts as a tester involve?

It involves integrating the default branch, reading Git conflict markers, combining test and product intent correctly, regenerating non-text artifacts, and verifying with builds and focused automated tests before pushing the branch.

Should QA engineers use merge or rebase?

Follow team policy. Merge preserves history with a merge commit; rebase replays your commits for a linear history. Both can produce conflicts, and both require the same careful file-level resolution and re-test.

How do I fix a package-lock.json conflict in a test repo?

Resolve package.json first, remove or regenerate the lockfile with npm install or your standard package manager command, run a bootstrap or smoke test, and commit package.json with the new lockfile together.

What are Git conflict markers?

They are the lines starting with conflict marker tokens that Git inserts around conflicting regions. You must choose the correct resulting code and delete every marker line before committing.

How can testers reduce merge conflicts?

Integrate main daily, keep pull requests focused, coordinate shared helper refactors, enforce one formatter, and avoid long-lived isolated test branches that drift from product code.

Is it safe to use git checkout --theirs for all conflicts?

No. Taking one side for every file can drop important assertions, waits, or CI steps from the other branch. Use whole-file ours/theirs only when you fully understand that one version should win.

What should I run after resolving conflicts in Playwright tests?

Search for leftover markers, run TypeScript or lint checks if applicable, execute the specs that cover the merged files, and only then push for full CI.

Related Guides