Interview prep · 227 questions

Automation testing interview questions

Updated

Selenium, Playwright, framework design and the flaky-test questions that separate someone who has maintained a suite from someone who has only written one.

55 questions

How do you decide what to automate and what to leave manual?

Automation Strategyjuniormidsenior

Automate what runs often against stable behaviour with an objective pass/fail: regression, smoke, data-heavy repeats, API contracts.

Automate what runs often against stable behaviour with an objective pass/fail: regression, smoke, data-heavy repeats, API contracts. Keep manual what needs judgment or keeps changing: exploratory work, visuals, usability, brand-new churning features.

The one-line ROI test: will the cost of writing and maintaining this be repaid in runs?

Real-world example

Checkout happy path: runs on every build, stable for a year, binary outcome — automated long ago. The new AI-suggestions panel: redesigned twice a month, quality is a judgment call — still manual, on purpose. Same product, opposite calls, both correct.

Key points
  • Automate: repeated + stable + objective
  • Manual: judgment or churn
  • ROI = write cost vs run count
They'll ask next · tap one for the answer
The trap

'Automate everything repetitive' with no mention of maintenance cost — maintenance is where automation budgets actually die.

Copy link

What should NOT be automated?

Automation Strategyjuniormid

Exploratory testing (its value is human judgment), usability and visual appeal, features still changing weekly, one-time checks, CAPTCHA/2FA-style flows designed to resist machines, and anything…

Exploratory testing (its value is human judgment), usability and visual appeal, features still changing weekly, one-time checks, CAPTCHA/2FA-style flows designed to resist machines, and anything where the automation would cost more than it returns.

Knowing what to skip is the difference between a suite and a liability.

Real-world example

We automated a PDF invoice's visual layout once — pixel comparisons. It failed on every font update, every OS render difference, every legitimate design tweak: dozens of false alarms, zero real bugs. Deleted after two months; a human glance per release did it better.

Key points
  • Exploration, visuals, churn, one-offs
  • Anti-bot flows resist by design
  • Cost > return = skip
They'll ask next · tap one for the answer
The trap

An empty list. 'Everything can be automated with effort' tells the interviewer you've never paid a maintenance bill.

Copy link

How do you calculate or argue the ROI of test automation?

Automation Strategymidsenior

Honestly: cost = writing + maintenance + infrastructure; return = manual execution time saved × runs, plus faster feedback and bugs caught pre-release.

Honestly: cost = writing + maintenance + infrastructure; return = manual execution time saved × runs, plus faster feedback and bugs caught pre-release. If a suite runs on every build, even expensive tests repay quickly; a test that runs quarterly may never repay itself.

The senior version: automation's biggest return is release speed — regression that took a week now takes an hour, so you ship weekly.

Real-world example

The argument that got budget: our 3-day manual regression ran monthly — 36 tester-days a year, and releases queued behind it. The suite cost ~20 days to build, a day a month to maintain, and let regression run nightly. It paid for itself before the year ended — and releases stopped queueing.

Key points
  • Cost: write + maintain + infra
  • Return: time × runs + speed
  • Feedback speed is the real prize
They'll ask next · tap one for the answer
The trap

Inventing a precise formula with fake numbers. Interviewers prefer the honest drivers plus one real payback story.

Copy link

Explain Selenium's architecture. What actually happens when you call driver.click()?

Selenium Coremidsenior

Your code talks to a browser driver (chromedriver, geckodriver) over the W3C WebDriver protocol — HTTP requests carrying JSON.

Your code talks to a browser driver (chromedriver, geckodriver) over the W3C WebDriver protocol — HTTP requests carrying JSON. driver.click() becomes a POST to the driver's local server; the driver translates it into the browser's own automation internals, the browser acts, and the response travels back.

Selenium 4 made W3C native, dropping the old JSON Wire translation layer.

Real-world example

Why this matters beyond trivia: that HTTP hop is why Selenium actions have latency, why a mismatched chromedriver/Chrome pair breaks everything, and why Playwright — which keeps a persistent socket to the browser instead of per-command HTTP — feels faster and flakes less on timing.

Key points
  • Code → driver (HTTP/W3C) → browser
  • click() = POST to the driver
  • Selenium 4 = native W3C
They'll ask next · tap one for the answer
The trap

Reciting 'Selenium has four components' without being able to trace one click end to end.

Copy link

What locator strategies does Selenium support, and how do you choose?

Locatorsjuniormid

ID, name, CSS selector, XPath, link text, partial link text, tag name, class name. Choosing is a stability ranking: dedicated test id first, then ID, then name, then a short CSS selector anchored to…

ID, name, CSS selector, XPath, link text, partial link text, tag name, class name. Choosing is a stability ranking: dedicated test id first, then ID, then name, then a short CSS selector anchored to something meaningful, XPath where you need text or DOM-walking.

The principle: locate by what the element IS, not where it happens to sit.

Real-world example

The same button, three ways: [data-testid=checkout-submit] survives redesigns; #submit-btn survives most changes; div.cart > div:nth-child(3) > button dies the day a banner is added above it. All three 'work' on day one — only the first two work in month six.

Key points
  • Test id > ID > name > CSS > XPath
  • Meaning over position
  • nth-child = future breakage
They'll ask next · tap one for the answer
The trap

Ranking by what's easiest to write instead of what survives change — the interviewer is asking about month six, not day one.

Copy link

CSS selectors vs XPath — which do you prefer and why?

Locatorsjuniormid

CSS by default: shorter, more readable, marginally faster, and it matches how developers think about the DOM.

CSS by default: shorter, more readable, marginally faster, and it matches how developers think about the DOM. XPath for the two things CSS can't do — matching on text content and walking up to a parent or across to a sibling.

It's not a war; it's a default plus a tool for the exceptions.

Real-world example

Finding the delete button in the row that contains 'John Smith': CSS can't see text, so XPath earns its keep — //tr[td[text()='John Smith']]//button[@aria-label='Delete']. For everything else on that page, the CSS versions are half the length and twice as readable.

Key points
  • CSS default: shorter, readable
  • XPath: text match + upward walk
  • Default + exception, not war
They'll ask next · tap one for the answer
The trap

A religious answer for either side. The senior answer is 'CSS by default, XPath for text and parents'.

Copy link

What is the difference between absolute and relative XPath?

Locatorsjunior

Absolute starts at the root and walks every step: /html/body/div[2]/div/form/input — one DOM change anywhere on the path breaks it.

Absolute starts at the root and walks every step: /html/body/div[2]/div/form/input — one DOM change anywhere on the path breaks it. Relative starts from anywhere meaningful with //: //input[@name='email'].

Absolute XPath in a code review is an automatic comment; there's no situation where it's the right choice.

Real-world example

Copy-XPath in DevTools hands you the absolute path — that's how they end up in codebases. The day marketing adds a cookie banner div at the top of body, every /html/body/div[2]/... in the suite dies at once. The relative //input[@name='email'] doesn't even notice.

Key points
  • Absolute: full path, brittle
  • Relative: // + attributes
  • DevTools copy-XPath = the source of sin
They'll ask next · tap one for the answer
The trap

Only defining them. Say the consequence: absolute breaks on any layout change, so it never passes review.

Copy link

How do you handle dynamic elements whose IDs change on every page load?

Locatorsmid

Anchor to what doesn't change: partial attribute matches (starts-with, contains) for the stable prefix of a generated ID, other stable attributes (name, role, aria-label, placeholder), stable text,…

Anchor to what doesn't change: partial attribute matches (starts-with, contains) for the stable prefix of a generated ID, other stable attributes (name, role, aria-label, placeholder), stable text, or a stable ancestor. Best fix of all: get test ids added.

Never anchor to the random part — that's writing tomorrow's flake today.

Real-world example

React app generating id='input-4f7a2': the stable half is the prefix — css=[id^='input-'] scoped inside #login-form. Better, the field had name='email' all along. Best, one PR later it had data-testid='login-email' and the locator problem stopped existing.

Key points
  • starts-with/contains on stable part
  • Prefer semantic attributes
  • Real fix: ask for test ids
They'll ask next · tap one for the answer
The trap

Regex-matching the random part — anchoring to exactly the thing that changes.

Copy link

Explain implicit, explicit and fluent waits. Which do you use?

Waits & Synchronisationjuniormidsenior

Implicit: one global setting — polls the DOM for any element lookup. Explicit: wait for a specific condition on a specific element — clickable, visible, text present.

Implicit: one global setting — polls the DOM for any element lookup. Explicit: wait for a specific condition on a specific element — clickable, visible, text present. Fluent: explicit plus custom polling interval and ignored exceptions.

Use explicit waits, always. Never mix implicit with explicit — the timeouts interact unpredictably and produce mystery delays.

Real-world example

A suite mixing a 10s implicit wait with explicit waits had checks that took 40 seconds to fail — the two mechanisms compounded. Dropped the implicit wait to zero, went explicit-only: same suite, failures in seconds, and the mystery delays vanished.

Key points
  • Explicit = condition on element
  • Never mix implicit + explicit
  • Playwright auto-waits (actionability)
They'll ask next · tap one for the answer
The trap

Not knowing the mixing problem — 'I use both' is the answer that fails this question.

Copy link

Why is Thread.sleep() (or time.sleep()) considered bad practice?

Waits & Synchronisationjunior

It waits a fixed time regardless of reality — too short on a slow day (flaky failure), too long on a fast day (wasted minutes, every run, forever).

It waits a fixed time regardless of reality — too short on a slow day (flaky failure), too long on a fast day (wasted minutes, every run, forever). A conditional wait exits the moment the condition is true and fails loudly when it never is.

Sleeps also hide the real question: WHAT are you waiting for? Name it, wait for it.

Real-world example

Audit of a 400-test suite found 130 sleeps averaging 3 seconds: six and a half minutes of pure waiting per run, forty runs a day. Replacing them with condition waits cut runtime by a quarter — and fixed two flakes whose sleeps were 'usually long enough'.

Key points
  • Fixed time vs actual condition
  • Slow day = flake, fast day = waste
  • Name what you're waiting FOR
They'll ask next · tap one for the answer
The trap

'It slows tests down' as the only reason — the deeper sin is nondeterminism: sleeps are why the suite lies.

Copy link

What causes a StaleElementReferenceException and how do you fix it?

Waits & Synchronisationmid

You held a reference to an element, then the DOM re-rendered — your reference points at a node that no longer exists. Classic after clicks that refresh a list, SPA re-renders, or any AJAX update.

You held a reference to an element, then the DOM re-rendered — your reference points at a node that no longer exists. Classic after clicks that refresh a list, SPA re-renders, or any AJAX update.

Fix: re-find the element after the change, don't cache element references across page updates, and wait for the re-render to finish before re-finding.

Real-world example

Loop over table rows, click 'archive' on each: first click succeeds, second throws stale — the click re-rendered the whole table, and the loop's saved row references died with it. Fix: re-query the rows each iteration, or archive by fresh locator every time.

Key points
  • DOM re-rendered under your reference
  • Re-find, don't cache
  • Common in loops over lists
They'll ask next · tap one for the answer
The trap

'The element was removed' half-answer — the point is YOUR REFERENCE died; the element is often right there, re-rendered.

Copy link

What is the Page Object Model and why use it?

Page Object Modeljuniormid

A design pattern: each page (or component) gets a class holding its locators and the actions you perform on it; tests call those methods and read like scenarios, not like DOM surgery.

A design pattern: each page (or component) gets a class holding its locators and the actions you perform on it; tests call those methods and read like scenarios, not like DOM surgery.

The payoff is one place to fix: when the login page changes, you update LoginPage, not forty tests.

Real-world example

Redesign moved the login form into a modal. Suite with POM: one file changed, green by lunch. The older scripts without it: the same three locators pasted across 40 tests, two days of find-and-replace, and a week of stragglers failing one by one.

Key points
  • Class per page: locators + actions
  • Tests read as scenarios
  • One change = one file
They'll ask next · tap one for the answer
The trap

Defining it without the maintenance argument — 'one place to fix' IS the answer.

Copy link

What belongs in a page object and what belongs in the test?

Page Object Modelmidsenior

Page object: locators, actions (login(user, pass)), state readers (getErrorText()). Test: the scenario and every assertion — arrange, act via page methods, assert on what the page reports.

Page object: locators, actions (login(user, pass)), state readers (getErrorText()). Test: the scenario and every assertion — arrange, act via page methods, assert on what the page reports.

The line that matters: page objects return state; tests judge it. Assertions inside page objects hide what a test actually verifies.

Real-world example

loginPage.login(user, wrongPass) then expect(loginPage.getError()).toContain('Invalid') — the test states the expectation. The anti-version, loginPage.loginAndVerifyError(), buries the assertion where nobody reading the test can see what's checked — and where every test inherits it whether wanted or not.

Key points
  • Pages: locators + actions + getters
  • Tests: scenarios + ALL assertions
  • Pages return, tests judge
They'll ask next · tap one for the answer
The trap

Assertions in page objects — the classic mid-level smell this question exists to detect.

Copy link

Walk me through the architecture of an automation framework you have built.

Framework Designmidsenior

Have a layered story ready: config per environment; a driver/browser factory; page objects and components; an API client for setup and teardown; test data builders; the tests themselves — thin,…

Have a layered story ready: config per environment; a driver/browser factory; page objects and components; an API client for setup and teardown; test data builders; the tests themselves — thin, scenario-shaped; and reporting with artifacts on failure, wired into CI.

Then one design decision you can defend: why fixtures over inheritance, why API-first setup, why parallel-safe data.

Real-world example

The decision interviewers dig into from mine: login happens through the API once per worker, injected as session state — the UI login test exists separately, and every other test starts logged in. That single choice cut suite time by a third and removed the most-repeated flake source.

Key points
  • Layers: config → driver → pages → data → tests → reporting
  • Thin tests, fat support
  • Defend one real decision
They'll ask next · tap one for the answer
The trap

Naming tools instead of structure. 'Selenium, TestNG, Allure' is a shopping list — layers and decisions are the architecture.

Copy link

What are the main types of automation framework?

Framework Designjuniormid

The textbook list: linear (record/playback), modular, data-driven (same test, external data sets), keyword-driven (actions as spreadsheet keywords), hybrid (mix), and BDD-layered (Gherkin on top).

The textbook list: linear (record/playback), modular, data-driven (same test, external data sets), keyword-driven (actions as spreadsheet keywords), hybrid (mix), and BDD-layered (Gherkin on top).

The honest addendum: real modern frameworks are hybrids — page objects + data-driven cases + fixtures. The taxonomy is interview vocabulary more than daily reality.

Real-world example

Our 'type'? Page-object structure, pytest parametrize feeding 40 boundary cases from data (data-driven), a thin BDD layer on exactly the five flows business stakeholders actually read, everything else plain code. Naming one bucket for that would miss the design.

Key points
  • Linear, modular, data/keyword-driven, hybrid, BDD
  • Reality: everything is hybrid
  • Say what YOURS mixes and why
They'll ask next · tap one for the answer
The trap

Reciting the six types with pride and no opinion — the follow-up 'which is yours?' exposes it immediately.

Copy link

How do you implement data-driven testing?

Data-Driven Testingjuniormid

One test body, many data sets: the runner injects rows — pytest's @parametrize, TestNG's @DataProvider, JUnit 5's @ParameterizedTest — from inline tables, CSV/JSON files, or builders.

One test body, many data sets: the runner injects rows — pytest's @parametrize, TestNG's @DataProvider, JUnit 5's @ParameterizedTest — from inline tables, CSV/JSON files, or builders.

Each row must report as its own named test result: 'boundary_18 passed, boundary_17 failed' — not one test that hides which of forty inputs broke.

Real-world example

Discount rules: one checkout test, a table of 30 rows — code, cart value, user tier, expected price. New rule? New row, no new code. When VIP50 broke, the report said exactly that row — the fix conversation started from the failure name alone.

Key points
  • One body, injected rows
  • parametrize / DataProvider
  • Each row = named result
They'll ask next · tap one for the answer
The trap

A loop inside one test — forty inputs, one result line, and no idea which row failed.

Copy link

Explain TestNG annotations and the order in which they execute.

Test Runnersjuniormid

Outside-in, suite to method: @BeforeSuite → @BeforeTest → @BeforeClass → @BeforeMethod → @Test → @AfterMethod → @AfterClass → @AfterTest → @AfterSuite.

Outside-in, suite to method: @BeforeSuite → @BeforeTest → @BeforeClass → @BeforeMethod → @Test → @AfterMethod → @AfterClass → @AfterTest → @AfterSuite.

Each scope pairs with what it wraps: suite-level for one-time global setup, method-level for per-test state like a fresh driver. Plus the workhorses: @DataProvider, groups, dependsOnMethods, enabled=false.

Real-world example

Typical wiring: @BeforeSuite starts the report writer, @BeforeClass logs in once for that page's tests, @BeforeMethod resets to the dashboard so every test starts equal, @AfterMethod screenshots on failure. Putting driver creation at suite level instead of method level is how tests start sharing dirty state.

Key points
  • Suite → Test → Class → Method, mirrored after
  • Scope matches setup lifetime
  • @AfterMethod = screenshot hook
They'll ask next · tap one for the answer
The trap

Wrong order under pressure — rehearse 'suite, test, class, method' until it's reflex.

Copy link

What are pytest fixtures and conftest.py?

Test Runnersmid

Fixtures are dependency-injected setup/teardown: declare a function with @pytest.fixture, request it by parameter name, yield the resource, clean up after the yield.

Fixtures are dependency-injected setup/teardown: declare a function with @pytest.fixture, request it by parameter name, yield the resource, clean up after the yield. Scopes control lifetime: function, class, module, session. conftest.py shares fixtures across a directory tree with zero imports.

It's composition instead of inheritance — the reason pytest frameworks stay flat and readable.

Real-world example

A browser fixture (session scope) feeds a logged_in_page fixture (function scope) that logs in via API and yields a ready page; tests just take logged_in_page as an argument. Teardown after yield closes cleanly even on failure — no BaseTest class, no super() chains.

Key points
  • Inject by parameter name
  • yield = setup/teardown split
  • conftest.py = shared, no imports
They'll ask next · tap one for the answer
The trap

Explaining fixtures as 'pytest's setup methods' — missing injection, scopes and conftest, which are the whole point.

Copy link

TestNG vs JUnit — what are the meaningful differences?

Test Runnersmid

Historically TestNG won on suite XML, groups, native @DataProvider, dependencies and parallel config — which is why Selenium-era frameworks standardised on it.

Historically TestNG won on suite XML, groups, native @DataProvider, dependencies and parallel config — which is why Selenium-era frameworks standardised on it. JUnit 5 closed most gaps: @ParameterizedTest, @Tag, extensions, parallel execution.

Today: new pure-Java projects often go JUnit 5 (ecosystem default); existing automation stacks stay TestNG — both are fine, and saying so is the senior answer.

Real-world example

The still-real difference I hit: TestNG's suite XML lets you compose exactly which groups run where — smoke on PRs, full nightly — without code changes. JUnit 5 does it with tags and build-tool config. Same outcome, different home for the wiring.

Key points
  • TestNG: suites, groups, dependencies
  • JUnit 5 closed most gaps
  • Both fine — reasons beat religion
They'll ask next · tap one for the answer
The trap

Answering from 2015 — claiming JUnit can't parameterise or tag is instantly dated.

Copy link

What is the difference between hard and soft assertions, and when do you use each?

Assertionsjuniormid

Hard assertions stop the test at first failure — right when the next steps are meaningless without this one passing.

Hard assertions stop the test at first failure — right when the next steps are meaningless without this one passing. Soft assertions collect all failures and report together at the end — right when verifying several independent facts on one screen.

Soft assertions MUST end with assertAll(), or they silently pass forever.

Real-world example

Order confirmation page: soft-assert name, total, address, delivery date — one run reports all four wrongs instead of fix-rerun-fix-rerun. But 'order was created' is a hard assert first: if there's no order, the other four checks are noise about a page that shouldn't exist.

Key points
  • Hard: stop — flow gates
  • Soft: collect — independent facts
  • assertAll() or silent pass
They'll ask next · tap one for the answer
The trap

Not mentioning assertAll() — the interviewer is specifically fishing for the silent-pass failure mode.

Copy link

What makes a good assertion?

Assertionsmid

It checks the OUTCOME the user cares about, not an implementation detail; it's specific (the value, not just non-null); and it fails with a message that diagnoses — expected vs actual vs where.

It checks the OUTCOME the user cares about, not an implementation detail; it's specific (the value, not just non-null); and it fails with a message that diagnoses — expected vs actual vs where.

Test the behaviour: after checkout, assert the order exists with the right total — not that some div got a CSS class.

Real-world example

Weak: assertTrue(orders.size() > 0) — fails as 'expected true'. Strong: assertEquals(order.total, 49.99) with context — fails as 'total was 54.99, expected 49.99, order #1042'. The second failure IS the bug report; the first is homework.

Key points
  • Assert outcomes, not internals
  • Specific values, not non-null
  • Failure message = diagnosis
They'll ask next · tap one for the answer
The trap

Only talking syntax. The question is really 'do your greens mean anything?'

Copy link

What causes flaky tests?

Flaky Testsmid

Races between test and app: acting before render/data settles (timing), tests sharing mutable state or data, order dependencies, environment drift, third-party dependencies mid-test, animations — and…

Races between test and app: acting before render/data settles (timing), tests sharing mutable state or data, order dependencies, environment drift, third-party dependencies mid-test, animations — and sometimes a real intermittent product bug wearing a flake costume.

Diagnose before assuming: some 'flaky tests' are correct tests catching a genuine race.

Real-world example

Our flakiest test 'randomly' failed asserting a dashboard count. Root cause: it read the count before the async refresh landed — sometimes fast enough, sometimes not. The fix wasn't a retry; it was waiting for the loading indicator to disappear. Timing, as usual.

Key points
  • Timing races = cause #1
  • Shared state and order deps
  • Some flakes are real bugs
They'll ask next · tap one for the answer
The trap

'Flaky tests are badly written tests' — sometimes the test is fine and the product races. Missing that misses real bugs.

Copy link

You have 50 flaky tests and the team now ignores red builds. What do you do?

Flaky Testsmidsenior

Restore trust first: quarantine the 50 into a non-blocking job — with tickets and owners, not a graveyard — so main goes green and red means something again, today.

Restore trust first: quarantine the 50 into a non-blocking job — with tickets and owners, not a graveyard — so main goes green and red means something again, today.

Then burn down by frequency: loop each flake with artifacts, fix the cause (waits, data isolation, order), return it to the blocking suite. And add a gate: new tests run N times before they're allowed in.

Real-world example

The order matters because trust dies fast and returns slowly: after quarantine, the first real failure on the now-green main was investigated within minutes — the habit of ignoring red reversed the same week, while the 50 were still being fixed in the background.

Key points
  • Quarantine now, with owners
  • Fix by failure frequency
  • Gate new tests (N clean runs)
They'll ask next · tap one for the answer
The trap

Jumping straight to fixing tests one by one — trust in the suite dies while you're on test #14. Quarantine first.

Copy link

Is automatic retry of failed tests a good idea?

Flaky Testsmid

As a diagnosis tool, yes; as a lifestyle, no. A retry that turns red into green hides a real signal — either a flaky test or an intermittent product bug — and both deserve investigation, not…

As a diagnosis tool, yes; as a lifestyle, no. A retry that turns red into green hides a real signal — either a flaky test or an intermittent product bug — and both deserve investigation, not suppression.

Defensible middle: retry once, but log and report every retried pass as 'flaky', tracked and burned down. Silent retries are how suites rot.

Real-world example

A payment test 'passed on retry' for months — everyone shrugged. When someone finally read the retry log, the first-attempt failure was a genuine race: double-submitting created two payment intents. Production found it the expensive way first. The retry had been muting a real bug.

Key points
  • Retries hide signal
  • If used: visible + tracked
  • Retried pass = flagged flaky
They'll ask next · tap one for the answer
The trap

A flat yes or flat no. The senior answer is the conditional: retry as instrumented tolerance, never as silence.

Copy link

How do you keep tests independent of each other?

Framework Designmidsenior

Each test creates its own state and cleans up: own data (unique per run — UUIDs, timestamps), own session, no reading what a previous test wrote, no ordering assumptions.

Each test creates its own state and cleans up: own data (unique per run — UUIDs, timestamps), own session, no reading what a previous test wrote, no ordering assumptions. Setup via API, teardown in fixtures that run even on failure.

The test of independence: any single test runs alone, and the whole suite passes shuffled and parallel.

Real-world example

The classic dependency: test A creates 'testuser@mail.com', test B logs in with it. Run B alone — fails. Run in parallel — A's teardown deletes the user mid-B. Fix: B creates its own unique user via API in two lines. Boring, bulletproof, parallel-safe.

Key points
  • Own data, unique per run
  • API setup, guaranteed teardown
  • Must pass shuffled + parallel
They'll ask next · tap one for the answer
The trap

'Run them in the right order' — order dependence IS the disease, not the cure.

Copy link

How do you handle test data in automation?

Data-Driven Testingmid

Three rules: each test OWNS its data (created per run, unique names, torn down after); creation happens below the UI (API or seeding — fast, reliable); and data setup lives in builders/factories so…

Three rules: each test OWNS its data (created per run, unique names, torn down after); creation happens below the UI (API or seeding — fast, reliable); and data setup lives in builders/factories so 'a user with an expired card' is one readable call.

Shared fixture data is the root of half of all parallel flakes.

Real-world example

Before: everyone tested against 'test_user_1', and Mondays were flake days — weekend runs left it dirty. After: makeUser(with_expired_card=True) builds a fresh one per test via API in ~200ms. Parallel runs stopped colliding the same day.

Key points
  • Own it, build it via API
  • Factories: readable one-liners
  • Unique per run = parallel-safe
They'll ask next · tap one for the answer
The trap

'We have a shared test account' — the answer that predicts every Monday-morning flake story.

Copy link

How do you handle authentication in every test without logging in through the UI each time?

Framework Designmidsenior

Log in once, below the UI: hit the auth API (or run one UI login per worker), capture the session — token, cookies, storage state — and inject it into each test's fresh context.

Log in once, below the UI: hit the auth API (or run one UI login per worker), capture the session — token, cookies, storage state — and inject it into each test's fresh context. Playwright's storageState makes this first-class.

Keep exactly one UI login test to cover the flow itself; everything else starts authenticated.

Real-world example

200 tests × 8 seconds of UI login = 27 minutes per run doing the same thing 200 times — with the login form as a 200-chance flake lottery. Switched to API login + storageState per worker: same suite, 27 minutes back, and login-related flakes went to zero.

Key points
  • Auth via API, inject session
  • storageState / cookie reuse
  • One real UI login test only
They'll ask next · tap one for the answer
The trap

'I put login in @BeforeMethod' — that still runs the UI 200 times; the question is about going below the UI.

Copy link

How do you run tests across multiple browsers?

Cross-Browser & Gridjuniormid

Parameterise the browser: a driver/context factory reads BROWSER from config, CI runs the suite as a matrix — chromium/firefox/webkit jobs in parallel.

Parameterise the browser: a driver/context factory reads BROWSER from config, CI runs the suite as a matrix — chromium/firefox/webkit jobs in parallel. Playwright projects make it declarative; Selenium uses a factory plus Grid or a cloud provider for the real spread.

Scope by analytics: full suite on your top browser, critical paths on the rest.

Real-world example

Reality check that impresses: our full 400-test suite runs on Chromium every build; Firefox and WebKit run the 60-test critical pack nightly. Full-matrix-everything sounds thorough and triples CI cost for bugs that almost never differ outside rendering and input edge cases.

Key points
  • Browser as config, not code
  • CI matrix / Playwright projects
  • Full on top browser, critical on rest
They'll ask next · tap one for the answer
The trap

Describing browser switching but no strategy for WHICH browsers get WHAT depth — the matrix without the judgment.

Copy link

What is Selenium Grid and when do you need it?

Cross-Browser & Gridmid

Grid runs your tests on remote browsers: a hub routes sessions to nodes with different browser/OS combos — RemoteWebDriver plus desired capabilities, same test code.

Grid runs your tests on remote browsers: a hub routes sessions to nodes with different browser/OS combos — RemoteWebDriver plus desired capabilities, same test code. Grid 4 modernised it (single binary, Docker, observability).

You need it for scale (parallel beyond one machine) or spread (OS/browser combos you don't have locally). Many teams now get both from Docker in CI or a cloud grid instead.

Real-world example

Honest sizing: for our Linux-only Chromium+Firefox needs, docker-compose in CI beat maintaining a Grid — two containers, done. Grid earned its keep at a client needing real Windows/Safari/legacy-Edge spread on-prem: hub plus five heterogeneous nodes, one suite.

Key points
  • Hub routes, nodes run
  • Same code via RemoteWebDriver
  • Docker/cloud often replaces it
They'll ask next · tap one for the answer
The trap

Explaining the hub/node diagram but unable to say when NOT to bother — 'Docker in CI covers us' is often the senior answer.

Copy link

Playwright vs Selenium — which would you choose and why?

Modern Toolsmid

New project, no constraints: Playwright — auto-waiting kills the biggest flake class, one API drives Chromium/Firefox/WebKit, trace viewer makes failures self-diagnosing, parallelism is built in.

New project, no constraints: Playwright — auto-waiting kills the biggest flake class, one API drives Chromium/Firefox/WebKit, trace viewer makes failures self-diagnosing, parallelism is built in. Selenium still wins on: every language binding, enormous ecosystem/talent pool, real Grid/legacy-browser needs, and existing investment.

The answer is context, stated out loud.

Real-world example

Two true stories: greenfield SaaS — Playwright, and the team stopped discussing waits entirely within a month. Bank with 800 Selenium/Java tests and Java-only hiring — staying put was correct; a rewrite would burn a year to re-arrive at parity.

Key points
  • Playwright: auto-wait, trace, speed
  • Selenium: languages, ecosystem, legacy
  • Context decides — say which
They'll ask next · tap one for the answer
The trap

Trash-talking either tool. Interviewers often maintain the one you're dismissing.

Copy link

What are Cypress's limitations?

Modern Toolsmid

Structural ones, from running inside the browser: same-origin restrictions (multi-domain flows are awkward), one browser tab — no true multi-tab/multi-window, no WebKit/Safari, JavaScript/TypeScript…

Structural ones, from running inside the browser: same-origin restrictions (multi-domain flows are awkward), one browser tab — no true multi-tab/multi-window, no WebKit/Safari, JavaScript/TypeScript only, and no native mobile.

In exchange you get a superb DX: time-travel debugging, automatic waits, instant reloads. Great inside its box; know the box's walls.

Real-world example

The classic wall: an OAuth flow bouncing to a third-party login domain and back. In Cypress that meant cy.origin gymnastics and stubbing; the same flow in Playwright was just… a test. If your product crosses domains constantly, that one difference decides the tool.

Key points
  • In-browser = same-origin box
  • No multi-tab, no Safari, JS-only
  • Superb DX inside the box
They'll ask next · tap one for the answer
The trap

'Cypress is bad' — it's opinionated. Limitations-with-tradeoffs is the answer; hate is not analysis.

Copy link

What is BDD, and what's your honest view of Cucumber?

BDDmidsenior

BDD is a collaboration practice: business, dev and QA agree behaviour as examples before code — Given/When/Then. Cucumber automates those examples via step definitions.

BDD is a collaboration practice: business, dev and QA agree behaviour as examples before code — Given/When/Then. Cucumber automates those examples via step definitions.

Honest view: transformative when stakeholders actually read/write the scenarios; pure overhead when it's engineers writing Gherkin for other engineers — a translation layer nobody asked for. The tool is fine; the misuse is epidemic.

Real-world example

Seen both: an insurance product where underwriters genuinely reviewed scenarios — Gherkin caught two rule misunderstandings before code, worth every step file. And a startup where QA wrote Gherkin nobody else ever opened: same tests, plus a regex layer to maintain. Same tool, opposite value.

Key points
  • BDD = shared examples first
  • Cucumber = the automation layer
  • No business readers → skip it
They'll ask next · tap one for the answer
The trap

'BDD is a testing tool' — it's a collaboration practice; the tooling without the collaboration is just ceremony.

Copy link

How do you handle dropdowns, alerts, iframes and multiple windows in Selenium?

Selenium Corejuniormid

Native selects: the Select class — byVisibleText/byValue. Custom dropdowns (most modern UIs): click to open, wait, click the option like any element.

Native selects: the Select class — byVisibleText/byValue. Custom dropdowns (most modern UIs): click to open, wait, click the option like any element. Alerts: driver.switchTo().alert() — accept/dismiss/getText. Iframes: switchTo().frame(...), work, then defaultContent(). Windows: capture handles, switchTo().window(newHandle), close and switch back.

The theme: know which CONTEXT you're in.

Real-world example

The classic hour-lost: 'element not found' on a locator that's visibly right there — because it lives inside an iframe and the driver is still on the parent page. Since then, first debugging question for any not-found: am I in the right frame?

Key points
  • Select class vs custom = click flow
  • switchTo(): alert / frame / window
  • Always switch back
They'll ask next · tap one for the answer
The trap

Only knowing the Select class — modern apps' dropdowns aren't selects, and the interviewer knows it.

Copy link

How do you handle file uploads and downloads in automated tests?

Selenium Corejuniormid

Upload: if there's an <input type=file>, sendKeys/setInputFiles the path — no dialog ever opens. Hidden inputs behind styled buttons: target the input anyway.

Upload: if there's an <input type=file>, sendKeys/setInputFiles the path — no dialog ever opens. Hidden inputs behind styled buttons: target the input anyway. Download: configure the browser profile to auto-save to a known folder, wait for the file, assert on it — or intercept the response/API directly.

Never automate the OS file dialog — it's outside the browser and outside your control.

Real-world example

A 'drag and drop only' uploader still had the file input under the styling — setInputFiles on it worked perfectly. For the export test, we skipped the browser entirely: the download button called a known endpoint, so the API test asserted content-type and CSV rows in 200ms.

Key points
  • sendKeys/setInputFiles the input
  • Downloads: profile dir or API
  • Never touch the OS dialog
They'll ask next · tap one for the answer
The trap

Reaching for AutoIt/Robot to click the OS dialog — the answer that says you didn't know about the input element.

Copy link

How do you capture screenshots and useful diagnostics when a test fails?

Reportingjuniormid

Centrally, in a failure hook — never per-test: an afterEach/listener grabs screenshot, page URL, browser console, and (Playwright) trace/video on failure, attached to the report.

Centrally, in a failure hook — never per-test: an afterEach/listener grabs screenshot, page URL, browser console, and (Playwright) trace/video on failure, attached to the report.

Goal: diagnose from the report alone. If the first response to red is 'run it locally', the diagnostics have failed.

Real-world example

Playwright's trace viewer changed on-call triage: a nightly failure at 3am used to mean reproduce-and-pray in the morning. Now the trace shows every action, DOM snapshot, network call and console line up to the failure — most reds are diagnosed in two minutes without a rerun.

Key points
  • Hook, not per-test code
  • Screenshot + console + trace
  • Diagnosable without re-running
They'll ask next · tap one for the answer
The trap

'I call takeScreenshot() in my test' — screenshots belong in the framework's failure hook, once, for every test.

Copy link

How do you integrate your automated tests into CI?

CI Basicsmidsenior

Staged by speed: on every PR, the fast gate — lint, unit, API, smoke (minutes, blocking). Full regression nightly or pre-release, parallelised across workers.

Staged by speed: on every PR, the fast gate — lint, unit, API, smoke (minutes, blocking). Full regression nightly or pre-release, parallelised across workers. Browsers via containers/matrix; secrets from CI vaults; reports and artifacts published where the team lives.

The design goal: feedback fast enough that developers don't route around the gate.

Real-world example

The number that matters: our PR gate is 6 minutes — devs wait for it. When it crept to 20, people started merging with '--no-verify culture' and bugs followed. We split the suite, moved depth to nightly, got back under 8 — compliance returned without a single policy email.

Key points
  • PR: fast blocking gate
  • Nightly: full depth, parallel
  • Slow gate = bypassed gate
They'll ask next · tap one for the answer
The trap

'Jenkins runs my tests nightly' as the whole answer — staging by speed and trust is what the question probes.

Copy link

What Git workflow do you use, and how do you resolve a merge conflict?

Version Controljuniormid

Feature branches off main, small PRs, review, squash-merge, main always releasable — plus rebase-before-PR to stay current.

Feature branches off main, small PRs, review, squash-merge, main always releasable — plus rebase-before-PR to stay current. Conflict resolution: pull latest main, rebase/merge locally, open each conflicted file, choose/combine between the <<< >>> markers understanding BOTH changes, re-run the tests, complete the merge.

The rule: never resolve code you don't understand — ask its author.

Real-world example

Test-code conflicts have a house speciality: two branches both edited the same page object — one renamed a locator the other's new test uses. Resolving 'ours' compiles and quietly breaks their test. That's why the re-run-tests step after resolution is non-negotiable.

Key points
  • Branch → small PR → squash
  • Understand both sides first
  • Re-run tests after resolving
They'll ask next · tap one for the answer
The trap

'I use git' with no workflow, or resolving conflicts by 'accept ours' — both are how other people's work disappears.

Copy link

What do you look for when reviewing someone else's test code?

Framework Designsenior

In order: can this test fail meaningfully — real assertions on real outcomes; independence — own data, no order coupling; stability — condition waits, no sleeps, sane locators; readability — the…

In order: can this test fail meaningfully — real assertions on real outcomes; independence — own data, no order coupling; stability — condition waits, no sleeps, sane locators; readability — the scenario is obvious; and placement — is UI even the right layer for this check?

Test code review is where suite quality is actually decided.

Real-world example

The review comment I make most: 'this asserts the API returned 200 — assert the ORDER exists with the right total.' Second most: 'this sleep is a race waiting to flake — what are we actually waiting for?' Both are five-minute fixes at review time and week-long hunts a month later.

Key points
  • Can it fail? Assertions first
  • Independence, waits, locators
  • Right layer for the check
They'll ask next · tap one for the answer
The trap

Style-guide answers — naming and imports — while missing 'does this test actually verify anything?'

Copy link

How do you decide which layer to automate a given check at?

Automation Strategysenior

Push every check to the lowest layer that can catch the bug: pure logic → unit; service behaviour and contracts → API/integration; only genuinely end-to-end journeys → UI.

Push every check to the lowest layer that can catch the bug: pure logic → unit; service behaviour and contracts → API/integration; only genuinely end-to-end journeys → UI. Lower is faster, stabler, and pinpoints failures.

The smell that triggers the question: a 40-minute UI suite where 30 minutes is re-testing business rules the API layer could verify in seconds.

Real-world example

Discount calculation had 25 UI tests — cart, code, assert the total, three minutes each. The rules moved to API tests: 25 cases in four seconds total. The UI kept exactly two: one user journey applying a code, one showing the error state. Coverage identical; feedback 40× faster.

Key points
  • Lowest layer that catches it
  • Logic→unit, rules→API, journeys→UI
  • UI = the journey, not the rules
They'll ask next · tap one for the answer
The trap

Reciting the pyramid without a migration example — the question is really 'have you MOVED checks down?'

Copy link

How do you test that an element is NOT present, without slowing your suite down?

Waits & Synchronisationmid

The trap is waiting a full timeout to prove absence. Right moves: first wait for a positive anchor — the state that proves the page settled — then assert absence instantly (findElements().isEmpty(),…

The trap is waiting a full timeout to prove absence. Right moves: first wait for a positive anchor — the state that proves the page settled — then assert absence instantly (findElements().isEmpty(), a short-timeout invisibility wait, or Playwright's expect(locator).toBeHidden(), which handles it natively).

Anchor first, then absence — fast and race-free.

Real-world example

'Deleted item disappears': naive version waited 10s for the row not to exist — every run, even passing ones. Fixed: wait for the 'Item deleted' toast (positive, instant when done), then assert the row list doesn't contain it — total cost ~200ms and no race window.

Key points
  • Anchor on a positive state first
  • Then absence check, short/no timeout
  • Playwright: toBeHidden auto-handles
They'll ask next · tap one for the answer
The trap

waitForInvisibility with a 10-second timeout as the whole answer — proving absence by paying full price every run.

Copy link

What is the Page Factory, and would you use it?

Page Object Modelmid

Selenium's annotation flavour of POM: @FindBy fields, initialised by PageFactory.initElements(), with lazy proxy lookup and optional caching.

Selenium's annotation flavour of POM: @FindBy fields, initialised by PageFactory.initElements(), with lazy proxy lookup and optional caching.

Would I use it? For new code, no — plain By locators (or Playwright locators) do the same with less magic: clearer stack traces, no stale-cache surprises from @CacheLookup, no proxy indirection when debugging. Recognise it, maintain it happily, don't start with it.

Real-world example

The debugging tell: with @FindBy proxies, a broken locator surfaces as a NullPointer-ish proxy failure two calls away from the cause; with a plain By, the exception says exactly which locator on which line. Ten minutes vs thirty seconds — magic always bills later.

Key points
  • @FindBy + initElements proxies
  • Same POM, more magic
  • New code: plain locators
They'll ask next · tap one for the answer
The trap

Presenting PageFactory as 'the professional POM' — it's an optional flavour, and modern practice moved past it.

Copy link

How would you automate testing of an application that has no test IDs and a constantly changing DOM?

Locatorssenior

Two tracks. Tactically: locate by user-facing semantics — roles, labels, accessible names, stable text — which survive re-renders because they're what the app MEANS.

Two tracks. Tactically: locate by user-facing semantics — roles, labels, accessible names, stable text — which survive re-renders because they're what the app MEANS. Playwright's getByRole/getByLabel is built for exactly this.

Strategically: make the case for test ids — one line per element, and the flake bill funds the argument. Testability is a feature request like any other.

Real-world example

A React app re-rendering constantly: every class-based locator died weekly. Switched to getByRole('button', {name: 'Submit order'}) style throughout — locator failures dropped to near zero, because roles and names only change when the product genuinely changes. Then the team added test ids anyway; the PR was 40 one-line changes.

Key points
  • Semantics: role + accessible name
  • Meaning survives re-renders
  • Parallel track: sell test ids
They'll ask next · tap one for the answer
The trap

Fancier XPath as the answer — cleverer brittleness is still brittleness; the escape is semantics, not selector golf.

Copy link

What is a headless browser, and what are the trade-offs?

Cross-Browser & Gridjuniormid

A real browser engine running without a visible window — same rendering, same JS, no GUI. Default for CI: faster startup, less memory, no display server needed.

A real browser engine running without a visible window — same rendering, same JS, no GUI. Default for CI: faster startup, less memory, no display server needed.

Trade-offs: you can't watch it (so artifacts matter more), and a few genuine behaviour differences exist — viewport defaults, focus/animation timing, historically some codecs/fonts. Rule: run headless in CI, debug headed, and mind the new-vs-old headless distinction in Chrome.

Real-world example

The classic headless ghost: a test green locally (headed), red in CI — element 'not visible'. Cause: headless default viewport 800×600 tucked the button under a collapsed menu. One line pinning viewport 1920×1080 in config ended a week of intermittent mystery.

Key points
  • Real engine, no window
  • CI default: fast, light
  • Pin the viewport; debug headed
They'll ask next · tap one for the answer
The trap

'Headless is faster' with no trade-offs — the viewport story is the difference between using it and understanding it.

Copy link

How do you structure assertions and reporting so that a failure is diagnosable without re-running the test?

Reportingsenior

Design for the 3am reader: specific assertions with expected/actual/context in the message; failure hooks attaching screenshot, console, network and trace; test names that state the scenario; steps…

Design for the 3am reader: specific assertions with expected/actual/context in the message; failure hooks attaching screenshot, console, network and trace; test names that state the scenario; steps annotated so the report reads as a narrative; and the build/commit under test stamped on the run.

Metric: what fraction of reds get diagnosed from the report alone. That number is the reporting quality.

Real-world example

Before/after one failure: 'AssertionError: expected true' vs 'Order total mismatch: expected 49.99, got 54.99 — order #1042, coupon SAVE10, trace attached'. The second needed zero reruns: the trace showed the coupon endpoint 500ing. Same bug, thirty seconds instead of an afternoon.

Key points
  • Messages: expected/actual/context
  • Artifacts auto-attached on failure
  • Report reads as a narrative
They'll ask next · tap one for the answer
The trap

'Good reporting = Allure installed' — the tool renders what your assertions and hooks provide; garbage in, pretty garbage out.

Copy link

A test passes locally but fails in CI. How do you investigate?

Flaky Testsmidsenior

Diff the two worlds, evidence first: pull CI's artifacts (screenshot, trace, logs) — usually the answer is visible.

Diff the two worlds, evidence first: pull CI's artifacts (screenshot, trace, logs) — usually the answer is visible. Then the usual suspects in order: timing (CI is slower — races surface), environment (headless, viewport, fonts, locale, timezone, versions), data (parallel tests colliding, dirty state), secrets/config.

Then reproduce CI's conditions locally — same headless, viewport, DB — rather than guessing.

Real-world example

Ours was timezone: a date-picker test asserting 'tomorrow' — local machine UTC+4, CI runner UTC. At 21:00 local, 'tomorrow' differed between the two worlds and the test failed only in CI, only in the evening. TZ pinned in config; the ghost died.

Key points
  • CI artifacts first
  • Suspects: timing, env, data, config
  • Reproduce CI's world locally
They'll ask next · tap one for the answer
The trap

'Rerun it in CI and see' — that's hoping, not investigating. Artifacts and diffing are the method.

Copy link

How do you test a feature that depends on a third-party service you can't control?

Framework Designmidsenior

Split the risk: your integration logic — test against mocks/stubs driving every response you care about: success, each documented error, timeout, slow-success, malformed body.

Split the risk: your integration logic — test against mocks/stubs driving every response you care about: success, each documented error, timeout, slow-success, malformed body. Their actual service — a thin scheduled contract/health check against the sandbox, isolated so its failures don't redden your suite.

Never let a partner's sandbox flake gate your merges.

Real-world example

Payment provider sandbox went down for an afternoon — before the split, 40 red tests and blocked PRs company-wide; nobody's code was wrong. After: suite green against WireMock, and one quarantined 'provider sandbox' check honestly reporting THEIR incident. Same afternoon, zero drama.

Key points
  • Mock for logic, all responses
  • Separate live contract check
  • Partner flake ≠ suite red
They'll ask next · tap one for the answer
The trap

'Test against their sandbox' as the whole answer — you've made your build depend on someone else's uptime.

Copy link

What is visual regression testing and when is it worth it?

Modern Toolsmidsenior

Screenshot-diff testing: capture pages/components against approved baselines, fail on pixel or perceptual drift — catching what functional tests are blind to: broken CSS, overlapping elements,…

Screenshot-diff testing: capture pages/components against approved baselines, fail on pixel or perceptual drift — catching what functional tests are blind to: broken CSS, overlapping elements, invisible-but-present buttons.

Worth it for stable, design-critical surfaces (marketing pages, design systems, checkout). Not worth it for churning UIs — every legitimate change becomes a baseline chore, and the noise buries the signal.

Real-world example

A CSS refactor broke the checkout button — still in the DOM, still 'clickable' to Selenium, visually white-on-white to humans. Every functional test passed. The screenshot diff caught it in the PR. That one save is the whole pitch; the counter-story is the team that abandoned visual tests under 30 false diffs a week.

Key points
  • Catches what functional can't see
  • Stable surfaces only
  • Baseline churn = death
They'll ask next · tap one for the answer
The trap

'Screenshot everything!' — visual testing's failure mode is self-inflicted noise, and the interviewer has lived it.

Copy link

How would you approach automating an application you've never seen before?

Automation Strategymidsenior

Learn before automating: explore the app, map the critical user journeys and the money path, inventory testability (test ids? stable APIs? auth options? environments?).

Learn before automating: explore the app, map the critical user journeys and the money path, inventory testability (test ids? stable APIs? auth options? environments?). Then start deliberately small — smoke on the critical path first, wired into CI from day one, then grow outward by risk.

Week one goal: five solid tests running on every build — not fifty specs on a laptop.

Real-world example

Day one at a new product: two hours of exploratory with the network tab open taught me the app was API-first with a thin UI — which flipped the plan to API-heavy coverage plus a handful of UI journeys. The framework decision came from the app's shape, not from my habits.

Key points
  • Explore + map journeys first
  • Testability inventory
  • Small smoke in CI, day one
They'll ask next · tap one for the answer
The trap

Starting with 'first I'd choose Playwright' — tool talk before understanding the app is the junior tell this question hunts.

Copy link

How do you keep an automation suite maintainable as it grows to thousands of tests?

Framework Designsenior

Architecture plus governance. Architecture: strict layers (tests thin, logic in pages/clients/fixtures), parallel-safe isolated data, tagging for selective runs, checks pushed to the cheapest layer.

Architecture plus governance. Architecture: strict layers (tests thin, logic in pages/clients/fixtures), parallel-safe isolated data, tagging for selective runs, checks pushed to the cheapest layer. Governance: review standards for test code, a flake budget with quarantine, deletion as a first-class activity, and suite-health metrics (runtime, flake rate) watched like product metrics.

Suites die of neglect economics, not size.

Real-world example

At ~2000 tests our nightly hit three hours. The fix wasn't hardware: 300 UI tests re-checking API-verifiable rules moved down a layer, 150 redundant/dead tests were deleted after a coverage audit, tagging split PR-smoke from nightly-depth. Runtime halved; trust went UP after deleting tests — that's the counterintuitive part worth saying.

Key points
  • Layers + isolated data + tags
  • Flake budget, quarantine, deletion
  • Watch runtime/flake like product KPIs
They'll ask next · tap one for the answer
The trap

Only architecture, no governance — thousand-test suites rot from unowned flake and unreviewed test code, not from folder structure.

Copy link

What's the difference between a test that's failing and a test that's broken?

Flaky Testsmidsenior

A failing test works perfectly: the product broke, the test caught it — that's the suite doing its job.

A failing test works perfectly: the product broke, the test caught it — that's the suite doing its job. A broken test fails for its own reasons — dead locator, bad wait, stale data — and says nothing about the product.

Triage is telling them apart fast: artifacts first, product behind the failure checked by hand or API. Fix broken tests; file bugs from failing ones. Confusing the two in either direction is expensive.

Real-world example

Two reds, same morning: one trace showed the app's 500 on checkout — failing test, real bug, escalated. The other showed a renamed button — broken test, locator updated in five minutes. Treating red #1 as 'probably flaky' would have shipped the 500; treating #2 as a bug would have burned a dev's afternoon.

Key points
  • Failing = product bug, test worked
  • Broken = test's own defect
  • Artifacts decide, fast
They'll ask next · tap one for the answer
The trap

Treating every red as 'flaky, rerun it' — the habit this question exists to screen out.

Copy link

What is Git, and why does version control matter for a tester?

Version Controljunior

Git is a distributed version control system — it tracks every change to your code, lets you branch and merge, and gives everyone a full copy of the history.

Git is a distributed version control system — it tracks every change to your code, lets you branch and merge, and gives everyone a full copy of the history. Version control is what lets a team work on the same codebase without overwriting each other, and roll back when something breaks.

For a tester it's day-one plumbing: your automation lives in Git, you branch to add tests, open a pull request, and your framework's history is part of the GitHub an interviewer scrolls before they meet you.

Key points
  • Distributed VCS: tracks history, branches, merges, full local copy
  • Lets a team share a codebase safely and roll back
  • Your test automation lives here — branch, PR, review, merge
They'll ask next · tap one for the answer
Copy link

What's the difference between merge and rebase?

Version Controlmid

Merge combines two branches and records a merge commit — history shows exactly what happened, branches and all.

Merge combines two branches and records a merge commit — history shows exactly what happened, branches and all. Rebase replays your commits on top of the target branch, producing a straight, linear history but rewriting your commit hashes.

Both are correct; teams pick one. The rule that matters: never rebase a branch others are working on — rewriting shared history breaks everyone's copy. Rebase your own local branch to tidy it before a PR; merge to integrate. Knowing why teams have an opinion here is the senior signal.

Key points
  • Merge: keeps true history + a merge commit
  • Rebase: linear history, but rewrites commits
  • Never rebase a branch others share — it breaks their history
They'll ask next · tap one for the answer
The trap

Force-pushing a rebased shared branch is the classic disaster — it rewrites history others have, and their next pull conflicts with everything. Rebase local, merge shared.

Copy link

How do you resolve a merge conflict?

Version Controlmid

A conflict means two branches changed the same lines and Git won't guess. It marks the file with <<<<<<< yours, ======= and >>>>>>> theirs.

A conflict means two branches changed the same lines and Git won't guess. It marks the file with <<<<<<< yours, ======= and >>>>>>> theirs. You open it, decide what the correct result is — keep one side, the other, or combine — delete the marker lines, then stage the file and continue the merge or rebase.

The discipline is understanding both changes before choosing, not blindly accepting one side. A conflict isn't an error; it's Git asking a human to make a decision it can't.

Key points
  • Two branches touched the same lines — Git needs a human
  • Edit the marked file, delete the <<< === >>> markers, stage, continue
  • Understand both sides before picking — don't blind-accept
They'll ask next · tap one for the answer
Copy link

What makes a good commit history — and why would an interviewer care?

Version Controlmid

Small, focused commits, each a coherent step, with a message saying what changed and why in the imperative — "Add smoke tests for checkout", not "fix" or "final".

Small, focused commits, each a coherent step, with a message saying what changed and why in the imperative — "Add smoke tests for checkout", not "fix" or "final". A stranger should read your git log and follow how the work was built.

Interviewers care because they scroll your GitHub before the interview, and your history is a writing sample you didn't know you submitted. A repo of five meaningful commits reads as an engineer; one commit called "solution" invites questions about where the code came from.

Key points
  • Small focused commits, imperative messages, what + why
  • The log should tell the story of how it was built
  • Your history is a writing sample interviewers actually read
They'll ask next · tap one for the answer
Copy link

What's the difference between git fetch and git pull?

Version Controljuniormid

git fetch downloads the latest commits from the remote but doesn't touch your working branch — you can inspect what changed before integrating.

git fetch downloads the latest commits from the remote but doesn't touch your working branch — you can inspect what changed before integrating. git pull is fetch plus an immediate merge (or rebase) into your current branch.

Pull is the everyday convenience; fetch-then-look is safer when you want to see what's coming before it lands on your work — especially before a rebase, or when you suspect the remote has moved in a way that'll conflict.

Key points
  • fetch: download remote changes, don't merge — inspect first
  • pull: fetch + merge/rebase into your current branch in one step
  • fetch-then-review when you want to see before you integrate
They'll ask next · tap one for the answer
Copy link
They'll ask next