TL;DR
- SDET interviews combine coding, test design and engineering judgment about which failures matter and where to detect them.
- Start with observable business behavior, then distribute checks across pure logic, API boundaries, integration points and a few essential user journeys.
- Treat a retry-pass result as evidence of a flaky test or system interaction that needs diagnosis, not proof that the first failure never mattered.
- Explain test data, isolation, authorization and cleanup alongside assertions; a test suite is a maintained software system.
What is an SDET expected to demonstrate?
A software development engineer in test builds tools and automated checks that help a team understand product risk. In an interview, that can mean writing a small function, finding a boundary case, designing an API test or explaining why a browser test fails intermittently in CI.
The eight original questions below use a fictional checkout service. A customer submits a cart, the server prices it, inventory is reserved and payment is requested. No real payment provider is used in these exercises. The JavaScript validation example was executed in local tests; the wider checkout scenarios are test-design proposals, not a claim that a production service has passed them.
Begin by clarifying the requirements. What counts as a successful order? Can inventory change during checkout? What happens when the client repeats a request? These answers determine the tests more than the choice of automation framework does.
1. How would you design a test strategy for checkout?
Define a small set of business invariants. A customer must not buy a different quantity from the one confirmed. The server must determine the price from trusted data. A retried request must not unintentionally create another charge. A customer must not read someone else's order.
Then place checks where they provide clear and timely evidence.
| Risk | Useful test location | Example observation |
|---|---|---|
| Invalid quantity accepted | Pure validation and API boundary | Zero or fractional quantity is rejected |
| Client changes a price | API or service integration | Server ignores or rejects client-supplied price fields |
| Repeated request creates another order | Service integration | Same idempotent operation resolves to one business order |
| Another customer's order is exposed | Authorization integration | Foreign order is not returned |
| Confirmation page shows the wrong state | Browser journey | Visible summary matches the persisted order |
| Payment result is uncertain | Integration with controlled provider behavior | Recovery reconciles state without blind duplicate charging |
The table is not a fixed ratio of unit, integration and browser tests. Choose the smallest reliable scope that can expose each risk, then keep a few end-to-end journeys that prove the important pieces connect. To present this strategy on a whiteboard, you can borrow the Scope, Risk, Layers, Signals and Exit structure from this quality engineer interview loop guide.
2. Can you write and test a small validation function?
For this exercise, a parsed JSON cart line must contain only sku and quantity. SKUs use uppercase letters, digits, underscores or hyphens, begin with a letter or digit, and contain at most 32 characters. Quantity must be an integer from 1 through 10. Unknown fields are rejected so a submitted price cannot silently become part of the accepted object.
function normalizeCartLine(input) {
if (input === null || typeof input !== 'object' || Array.isArray(input)) {
throw new TypeError('Cart line must be an object');
}
const keys = Object.keys(input);
if (keys.length !== 2 || !keys.includes('sku') || !keys.includes('quantity')) {
throw new TypeError('Expected only sku and quantity');
}
if (typeof input.sku !== 'string' || !/^[A-Z0-9][A-Z0-9_-]{0,31}$/.test(input.sku)) {
throw new TypeError('Invalid SKU');
}
if (!Number.isInteger(input.quantity) || input.quantity < 1 || input.quantity > 10) {
throw new RangeError('Quantity must be an integer from 1 to 10');
}
return { sku: input.sku, quantity: input.quantity };
}Test the exact boundaries: quantities 1 and 10 succeed; 0, 11, a fraction and the string "2" fail. An empty SKU, a lowercase SKU and a 33-character SKU fail under this particular contract. A 32-character valid SKU succeeds. Also verify that a new object is returned without mutating the input.
Now state what the function does not prove. It does not establish that the SKU exists, inventory is available or the caller is allowed to order. Those require other checks. The input contract is parsed JSON data, not arbitrary JavaScript objects with getters or custom prototypes. If that contract changes, the implementation and threat model need another review.
3. What would you assert in an API test beyond the status code?
A 201 response is only one observation. Verify the response shape and business fields, then inspect the resulting state through an appropriate authorized read or test fixture. Did the expected customer own the order? Was the server-calculated total stored? Was inventory reserved exactly once?
Include negative cases with equally clear expectations. An invalid quantity should not leave a partial order behind. An unauthorized caller should not receive private order details. If the API deliberately uses a not-found response to avoid disclosing resource existence, test that contract rather than assuming every denial must use the same status.
Playwright's API testing documentation shows how request contexts can make API calls and prepare or inspect state around browser tests. The framework does not decide the correct business assertion for you.
Use unique test identifiers and make cleanup safe to repeat. A test that passes only because an old order happens to exist in a shared environment is not giving dependable evidence.
4. How would you test retries and concurrent checkout requests?
Separate a repeated delivery of one operation from two distinct orders. In the fictional service, define how an idempotency key is scoped and what happens if the same key is reused with different request data. The expected behavior must come from the API contract.
Create a test that sends concurrent copies of the same operation and checks the durable outcome, not only two response bodies. Then test an uncertain provider response: the payment request may have succeeded even though the caller did not receive confirmation.
A controlled provider double can produce timeout-before-processing, timeout-after-processing and explicit rejection. Those cases need different recovery logic. Verify how the service reconciles an uncertain result before it creates another side effect. For a related server-side case, the idempotent payment question in this backend engineer interview question bank covers a process that crashes after the charge succeeds but before its response is stored.
Use synchronization in the test to make the overlap meaningful. Two requests launched one after the other may never exercise the race you intended. Also explain the limits of the test environment; a local double cannot establish a real provider's behavior outside its documented contract.
5. A browser test passes on retry. What do you do?
Capture the first failure, including trace, network behavior, relevant logs and test data. Look for shared state, a locator matching the wrong element, an unawaited action, inconsistent timing or an actual application race.
The Playwright retry documentation distinguishes tests that pass immediately from flaky tests that fail and then pass on retry. Retrying can help collect evidence or reduce disruption, but it should not erase the original failure from the team's quality signal.
Avoid adding a large fixed sleep as the first repair. Identify the condition the test should wait for. If the user-visible state never becomes correct, a longer delay only makes the failure slower.
Compare isolated and suite runs. A test that fails only after another test may share an account, a cart, storage or database rows. Repair the dependency or fixture boundary, then verify the original failure pattern no longer reproduces under a relevant repeated run.
6. How do you make browser tests maintainable?
Express behavior through stable user-facing locators and assertions where appropriate. A test that depends on a long chain of layout selectors can fail when the page is rearranged even though the user's task still works.
Playwright's best-practice guidance emphasizes isolated tests, resilient locators and its waiting behavior. Those principles still require judgment. A button named “Confirm” may be ambiguous if two dialogs are open; scope the locator to the intended interaction.
Keep fixtures responsible for setup and teardown, and keep the test body focused on the behavior being checked. Avoid a giant helper that hides every click and assertion behind one unexplained operation. When a test fails, the next engineer should be able to identify the failed expectation and the state needed to reproduce it.
Finally, choose which dependencies should be real. Mocking everything can miss integration failures; using every external service in every test can make feedback slow and unreliable.
7. How should tests influence a CI release decision?
Identify the checks that protect the changed behavior and the broader checks required by the team's release process. A cart-validation change should exercise accepted and rejected inputs as well as its API boundary. A payment-state change needs recovery and concurrency coverage.
Keep test results tied to the artifact being released. If the build changes after a passing run, the earlier result does not automatically validate the new artifact. Retain failure evidence without exposing credentials or customer data in logs.
For a known flaky test, assign an owner and a repair plan rather than silently excluding it forever. A temporary quarantine should leave the untested risk visible. Explain what compensating evidence supports the release while that check is unavailable.
Coverage percentages can identify code that was not exercised, but a high percentage does not establish that the assertions protect the business behavior.
8. How would you communicate a defect to a developer?
Provide the observed behavior, expected behavior, minimal reproduction, environment and impact. Distinguish a confirmed failure from a suspected cause. The hypothesis, prediction and test loop recommended for live debugging interviews is a practical way to keep the two apart.
For example: “Two concurrent requests with the same key created two persisted orders in the integration fixture. The contract requires one operation. Here are the request IDs and the two resulting order IDs.” That is more actionable than “idempotency is broken sometimes.”
Include a regression test when practical, but keep it focused on the externally meaningful invariant. A test that merely copies the implementation's internal steps may fail to catch the original defect after a refactor.
Rehearse the code and the reasoning together
Run the validation function against the boundary cases, then explain the checkout table without notes. Ask a partner to add one uncertainty, such as a provider timeout after processing, and revise the test plan aloud.
Use PhantomCodeAI for permitted interview practice if you want help structuring that explanation. The goal is to show what evidence each test provides, what remains untested and how you would investigate the next failure.