Interview Q&A · 28 questions · 6 answered publicly
Testing Interview Questions
Ordered by how often they're asked. Sign in free to see every answer, rate your confidence and run timed mock interviews.
-
Most asked What is the test pyramid, and is it still relevant?
Summary
The test pyramid recommends many fast unit tests, fewer integration tests, and a small number of slow end-to-end tests, because cost and flakiness rise toward the top. It's still a useful guide, though many teams now favor a 'testing trophy' with more integration tests, since they catch wiring bugs that unit tests miss.
Longer Answer
The underlying principle matters more than the shape: get the most confidence per unit of execution time and maintenance cost. Test behavior at the boundary where bugs actually appear, and keep E2E tests for critical user journeys.
Key Points
- Many unit, fewer integration, few E2E
- Cost and flakiness rise upward
- Testing trophy favors integration tests
- Optimize confidence per cost
Comparison / Tradeoffs
Level Speed Scope Typical tools Unit ms One function/class pytest, JUnit, Vitest, PHPUnit Integration ms–s Module + DB/HTTP Testcontainers, TestClient, MockMvc E2E s–min Full system via UI/API Playwright, Cypress -
Most asked What makes a good unit test?
Summary
A good unit test is fast, deterministic, isolated, and readable; it tests one behavior through the public interface, has a descriptive name, follows Arrange-Act-Assert, and fails for exactly one reason with a clear message.
Longer Answer
Avoid testing implementation details (private methods, exact call sequences) so refactoring doesn't break tests. Prefer real collaborators when they're cheap and deterministic; mock only boundaries such as networks, clocks and randomness.
Code Example
def test_discount_applies_to_orders_over_100(): # Arrange order = Order(items=[Item(price=120)]) # Act total = apply_discount(order, percent=10) # Assert assert total == 108Key Points
- Fast and deterministic
- Tests behavior, not implementation
- Arrange-Act-Assert
- Descriptive names
-
Most asked What's the difference between mocks, stubs, fakes and spies?
Summary
Stubs return canned answers; mocks additionally verify that expected interactions happened; fakes are working lightweight implementations (in-memory repository); spies wrap real objects and record calls. All are 'test doubles'.
Longer Answer
Overusing mocks couples tests to implementation and gives false confidence when the mocked contract drifts from reality. Prefer fakes for repositories and real infrastructure via containers for integration tests.
Key Points
- Stub: canned responses
- Mock: verifies interactions
- Fake: simplified real implementation
- Spy: records calls on real object
- Don't over-mock
Comparison / Tradeoffs
Double Returns data Verifies calls Has logic Stub Yes No No Mock Yes Yes No Fake Yes No Yes Spy Real Yes (records) Real -
Most asked How do you write reliable integration tests that use a database?
Summary
Run the real database engine (via Testcontainers or a CI service container), apply migrations, isolate each test (transaction rollback, truncation, or unique data), and seed data with factories rather than shared fixtures.
Longer Answer
SQLite as a stand-in for PostgreSQL misses differences in types, locking, JSON and constraints. Keep tests independent so they can run in parallel and in any order.
Key Points
- Use the real engine
- Apply migrations in tests
- Isolate per test (rollback/truncate)
- Factories over shared fixtures
-
Most asked What causes flaky tests and how do you fix them?
Summary
Flaky tests pass and fail without code changes. Common causes: timing and race conditions, shared state between tests, test order dependence, real network or time dependencies, randomness, and resource limits in CI.
Longer Answer
Fixes: wait for conditions instead of sleeping, isolate data, freeze time and seed randomness, mock external networks, and run tests in random order locally to catch dependencies. Track flakiness and treat it as a bug; automatic retries hide real problems.
Key Points
- Timing/race conditions
- Shared state and order dependence
- Uncontrolled time/network/randomness
- Track and fix; don't just retry
-
Most asked Is 100% code coverage a good goal?
Summary
Not usually. Coverage shows which code was executed, not whether behavior was verified; chasing 100% leads to low-value tests of trivial code. Use coverage to find untested critical paths, and set a reasonable floor or a 'no decrease' rule.
Longer Answer
Branch coverage is more informative than line coverage; mutation testing (Stryker, PIT, mutmut, Infection) measures whether tests actually detect changes.
Key Points
- Executed ≠ verified
- Find gaps in critical code
- Branch over line coverage
- Mutation testing measures test quality
- What should end-to-end tests cover, and how do you keep them stable?
- What is test-driven development (TDD) and when is it useful?
- What is contract testing?
- How do you test code that depends on the current time or randomness?
- How do you test code that calls external HTTP services?
- How should you test frontend components?
- How should you manage test data?
- How do you test asynchronous code?
- What is property-based testing?
- What are snapshot tests, and what are their risks?
- How do you prevent regressions when fixing bugs?
- Where do performance tests fit in a test strategy?
- How do you include security testing in development?
- What are ephemeral (preview) environments and why use them?
- What is chaos engineering?
- How do you keep a large test suite fast?
- How do you add tests to legacy code that wasn't designed for testing?
- What should API tests verify?
- How do you test code behind feature flags?
- What are smoke tests?
- What is mutation testing?
- How do you test accessibility?