Guides · Roles and seniority

SDET interview: writing test cases for a file upload, done properly

2026 · ~10 min read · OfferPilot AI

The SDET test case design question is scored on how many dimensions you cover, not how many cases you list. Ten cases across ten dimensions beats forty that are all input validation. Below is one feature, a profile photo upload, taken through the full enumeration in the order a strong candidate says it out loud: equivalence classes, boundaries, state transitions, races, failure injection, security, cross-browser divergence, accessibility, and what you would deliberately not automate. Then the follow-up interviewers have started adding at the end, answered straight: when would you trust a test case an AI wrote.

What the interviewer is listening for

Three tells separate a rehearsed list from a working method. The first is order. Partitions come before boundaries, because a boundary is only meaningful once you know which partition it divides. Candidates who open with "what if the file is corrupt" are free-associating, and it shows by minute four.

The second is the oracle. For every case, say what the expected result is and where that expectation comes from. "Upload a 6 MB file" is not a test case. "Upload a 6 MB file, expect a rejection before any bytes leave the browser, with the limit stated in the message" is one.

The third is cost. Saying what you would not test, and why, is the senior signal. Nobody gets credit for a plan that would take three weeks to automate.

Before enumerating anything, ask about the spec. Every one of these is a real fork in the test plan:

  • Does the 5 MB limit mean 5,000,000 bytes or 5,242,880 bytes? Different tests, and the answer decides two of your boundary values.
  • Is the type check on the file extension, on the MIME type the browser reports, or on the file's actual leading bytes? Three different bug classes.
  • Does "replaces the old photo everywhere" mean immediately, or eventually once a cache expires? That single word decides whether stale-image reports are bugs.
  • Does the old object get deleted, or orphaned in storage forever?

The feature, then the ten passes

Users can set a profile photo. Drag and drop onto the avatar, or click to browse. JPEG and PNG only, one file, 5 MB maximum. A progress bar shows while it uploads. The new photo replaces the old one everywhere it appears in the product.

Copy the ten headings below. They are the template. Swap the feature and the passes still hold.

  1. Requirements and oracles. Say the ambiguities above out loud first. If nobody can tell you the expected value for a case, that is a defect in the specification, and naming it earns more than another case would.
  2. Equivalence classes. Partition every input dimension, then pick one representative from each. File content: a real JPEG, a real PNG, a GIF, a PDF renamed to .png, an SVG, a file with a valid header and a truncated body. Count: zero files, one, several selected at once in the picker. Source: picker, drag, clipboard paste, mobile camera. Session: signed in, signed out, session expiring mid-upload, a user without permission. Prior state: has a photo already, has none.
  3. Boundaries. Now get numeric. Zero bytes. One byte. Exactly 5,000,000 and exactly 5,242,880, which smokes out the megabyte ambiguity in a single pair of tests. One byte either side of whichever the spec meant. A 1 by 1 pixel image. The largest pixel dimensions the resizer accepts, and one pixel past. Filename at 254, 255 and 256 bytes, then the same three lengths counted in characters using a name full of multibyte characters, because those are not the same limit.
  4. State transitions. Sketch the machine, idle to selected to uploading to processing to done, then test only the edges nobody drew. Cancel from each state. Navigate away mid-upload and come back. Pick a second file while the first is still uploading. Drop a file on the zone while a modal is open. Fail, retry, succeed. Upload and then immediately delete. Press browser back after success. Pick the same file twice in a row, the classic case where nothing happens at all because the input value never changed.
  5. Concurrency and ordering. Two tabs signed in as the same user upload different photos at the same moment: last write wins, or a lost update? Cancel at 99% while the server completes anyway: is the photo set? Two uploads where the first response arrives after the second: does the UI settle on the older image? Double-click Save. Upload the identical file twice, deduplicated by hash or stored twice? Delete the old object while a request for it is still in flight.
  6. Failure injection. Cut the network at 90%, then restore it: does it resume, restart, or hang forever behind a bar that never moves? Force a 500 from storage. Expire the presigned URL so the PUT returns 403. Make the resize worker time out after the upload returned 200, so the UI says done and the image never appears, which is the failure users actually report. Throttle to slow 3G and check the progress bar is measuring bytes rather than animating. Exceed the storage quota. Have the malware scanner quarantine the object asynchronously.
  7. Security and abuse. Make extension, reported MIME type and actual leading bytes disagree in all three directions. An SVG containing a script tag, served from an origin that shares cookies. A decompression bomb. Path traversal in the filename. EXIF GPS coordinates surviving into the stored file. Then the access check: user B changes an id in the request and tries to overwrite user A's photo. Confirm the served image comes back with a content type you set, not one you echoed from the request.
  8. Cross-browser and device. The cases in the table below, the ones that pass in one engine and fail in another.
  9. Accessibility and input method. The drop zone has to be reachable and operable with the keyboard alone, because dragging is not an input method available to everyone. Progress must be announced, not only drawn. Focus must land somewhere sensible when the dialog closes. Error text must be programmatically associated with the control rather than floating in red nearby. Check at 200% zoom and at a 320 pixel viewport.
  10. Non-functional, and what you skip. Name a p95 target for a 4 MB upload on a connection your users really have. Confirm the failure path emits a log line carrying the request id. Confirm the failure-rate metric exists before launch, not after the first incident. Then say the quiet part: you would not automate the drag gesture in a real browser, because it is the flakiest and most expensive test in the set. Validation goes in unit tests, the upload contract goes in API tests, and exactly one end-to-end test goes through the file picker.

Written out in full that is roughly sixty to eighty cases. In a 45-minute round you say the ten headings in about four minutes, then go deep on the two or three the interviewer points at. Offering the map and letting them choose the territory is the behaviour being graded.

The cross-browser cases that pass in one engine and fail in another

Engine behaviour here has shifted over the years, so treat this as a list of places to look rather than a list of guarantees, and verify against the browser versions in your actual support matrix.

CaseHow to trigger itWhat you assert
Folder dropped on the zoneDrag a directory, not a file, onto the avatarDirectory entries are exposed through a non-standard API in some engines and not others, so the same drop can yield zero files, one unusable entry, or silence. Assert an explicit message, never a no-op.
Picker opened after an awaitClick a custom button that does async work, then calls click() on the hidden inputUser activation rules differ and Safari is stricter. The picker opens in Chrome and does nothing in Safari. Assert it opens from a direct handler with no awaited work in front of it.
Photo from an iPhoneUpload from iOS Safari, once from Photos and once from FilesWhether a HEIC or a converted JPEG reaches your server depends on iOS version and how the file was chosen. Assert the server handles both, or rejects one with a message a human can act on.
Non-ASCII filenameUpload a file whose name carries accents and an emojiMultipart filename encoding and what your framework decodes both vary. Assert the stored name round-trips, or is safely replaced, and never truncates mid-character.
Progress eventsUpload a 4 MB file on a throttled connection in each engineUpload progress is available through XHR, but request body streaming with fetch is not universal, so a fallback path shows 0% and then jumps to 100%. Assert monotonic intermediate values.
Private window or cleared storageStart a resumable upload, then clear site data, then repeat in a private windowResume tokens kept in browser storage can vanish under stricter storage policies. Assert a clean restart rather than a stuck bar.

The accept attribute belongs in this section too. On every engine it filters the picker, on most platforms the user can switch back to All Files, and dragging bypasses it entirely. It is a convenience, never a control.

When would you trust an AI-generated test case?

The one-sentence answer, and the one worth saying in the room: trust it for the enumeration, never for the oracle.

Generation is genuinely good at the part humans do worst, producing the tenth partition after enthusiasm ran out at the fourth. Paste the spec, ask for boundary values, and 5,242,880 comes back without you having to remember whether the limit was decimal or binary. Ask for failure modes and you get the asynchronous quarantine case you would have skipped. Use it as a checklist you edit, and keep only the entries you can justify.

The expected result is a different thing entirely. A test case is a claim about intended behaviour, and a model does not know your product's intent. It knows what a system shaped like yours usually does. When the generator writes both the input and the assertion, what you get is a test that agrees with whatever the code does today, so it passes on a broken build and pins that bug in place permanently. That is worse than having no test, because it occupies a slot in the suite and buys confidence nobody earned.

Three checks turn a generated case into a real one:

  • Can you point at the source of the expected value? A spec line, a ticket, a decision a person made. If the only source is the generator, it is not a test yet.
  • Has it ever gone red? Break the code deliberately and run it. A generated test that has never failed is a comment with a runtime cost.
  • What does it actually assert? The three default shapes are "does not throw", "status is 2xx", and a snapshot of current output. All three pass on almost any behaviour, correct or not.

State a policy, not an opinion about the tools. Something like: I use it for the enumeration pass, I review every expected value by hand, and I do not let it write assertions for anything touching money, permissions or deletion. That is a complete answer. Praising the tools is not, and dismissing them reads as someone who has not tried them.

Where a live copilot is the wrong tool for this round

Worth saying plainly, since this page is published by a vendor. Test case design is a four-minute structured monologue followed by interruptions, and it is often conducted in a shared document while the interviewer watches you type. OfferPilot AI listens to the interviewer's audio and drafts a speakable answer in roughly a second, which fits a rapid-fire factual question well and fits a ten-heading taxonomy badly. Reading an enumeration off a screen sounds exactly like reading an enumeration off a screen, and the first follow-up, why 5,242,880, lands on nothing. Learn the ten passes instead. Save live assistance for the short factual questions in the same loop, flaky test triage or why an API test is slow, and for the coding portion of an SDET screen, where a discrete problem has a discrete answer.

The overlay is excluded from screen capture at the operating system level, so it does not appear in a Zoom, Teams, Meet or OBS share, or in a screenshot, while staying visible on your own monitor. That covers screen sharing and screen recording only. It does nothing about a webcam feed, a lockdown browser, or an interviewer sitting beside you at an onsite, which is common in SDET loops. Windows 10 and 11 only, with no macOS, Linux or mobile build.

The higher-leverage preparation sits upstream of the interview anyway. Build a question bank for the specific company and role with the company Q&A generator, and rehearse the enumeration against whatever that company actually ships, an upload flow, a payments form, a scheduling grid, until the ten headings come out in order without effort.

Free tier is 5 minutes of listening time a day, no card required, and the clock advances only while the copilot is actually receiving interviewer audio. Download for Windows.

Related guides