If every end-to-end test starts from an injected session, nothing exercises the real sign-in flow any more. How do you cover login itself — including a third-party OAuth redirect and a multi-factor prompt — without paying for it in every test?
answer
- injection removes login from coverage
- one deliberate test owns the real flow
- the redirect-back path is never injected
- TOTP is a function, not a phone
- emailed codes need a test mailbox API
basics
~20 sCover the real flow in a small number of dedicated tests and inject the session everywhere else. Make those tests deterministic: a dedicated identity-provider test account, and a multi-factor code derived in the test from the account's shared TOTP secret rather than read from a phone.
solid answer
~50 sSplit the concern. Login is *setup* for nearly every test and *the subject* of a few, so keep one small suite that drives the genuine flow — credentials, the provider redirect, the second factor, the landing page — and let everything else restore a saved session. The hard part is determinism. For third-party OAuth, use a dedicated test account on the real provider (or a sandbox tenant), because automated sign-ins against a consumer identity provider attract rate limits and bot challenges; some teams run the callback against a local mock OIDC provider instead and accept that they are then testing their own callback handling, not the provider. For MFA, seed the test account with a known TOTP shared secret and compute the current code in the test with a standard library, which is what makes an otherwise unautomatable step reproducible. Where the second factor is emailed or texted, read it from a test mailbox API rather than a real inbox.
go deeper
Know that injecting a session skips the login screen, so at least one test must still sign in for real, and that a test account's second factor is set up specifically to be automatable.
Explain how a TOTP code is derived from a shared secret in the test, why the code must be generated immediately before submitting, and where an emailed code has to be read from instead.
Show the coverage design: which few paths the real-flow suite owns — including the redirect back to the requested page — and the fidelity tradeoff between a real provider tenant and a local mock OIDC server.
Own the risk call: how much external dependency belongs in the merge gate, what a provider outage should be allowed to block, and how the team keeps a rarely-run authentication suite trusted rather than quietly disabled.
## The gap injection creates Programmatic login is the right default, but it has a cost that is easy to leave unpaid: the sign-in flow stops being exercised. Because the tests still pass, nobody notices until a release ships a login page that cannot log anybody in. The discipline is to make the coverage *explicit* rather than incidental — a named, deliberate set of tests whose subject is authentication. ## What that small suite should actually assert Keep it tight. Roughly: - The happy path: real credentials, real submit, land on the authenticated landing page as the right user. - The rejection path: wrong credentials produce the error state, and the user is not authenticated afterwards. - The redirect-back path: a deep link to a protected page bounces to login and, after signing in, returns the user to the originally requested URL. This is the one most often broken by a refactor and the one injection can never cover, because injected tests never traverse the guard. - The federated path, once: the provider redirect completes and the app establishes its own session. - The second factor, once: the prompt appears and a correct code completes sign-in. Everything else about the authenticated app is covered by injected-session tests. Resist the pull to add "and while we're logged in, let's check the dashboard" to these tests — that recouples the suite to the login screen. ## Third-party OAuth without the flake A redirect to an external identity provider takes the browser out of your application entirely, and that is the problem: you now depend on someone else's uptime, layout, locale, rate limits and bot detection. Three options, in rough order of preference: 1. **A dedicated test tenant or sandbox on the real provider.** Highest fidelity. Use a purpose-made account with a stable password and no interactive challenges, and accept that this test is slower and should not run on every commit. 2. **A mock OIDC provider running locally.** You control the authorization endpoint, so it issues an ID token instantly and deterministically. Be honest about what this covers: your redirect construction, your callback handler, your session establishment and your state/nonce handling — not the provider's behaviour or its consent screen. 3. **Skipping the provider and injecting the post-callback session.** Fine for the rest of the suite, but it means federated login has no end-to-end coverage; if the app has no password login at all, that is a real hole and one of the first two options is required. One subtlety worth naming: a federated flow ends with *your* application issuing its own session. Everything after that boundary — the app session, its expiry, its refresh — is yours to test and is exactly what injected-session tests exercise. That is why one federated test is usually enough. ## Making MFA deterministic A time-based one-time password looks unautomatable — it is on someone's phone. It is not, because TOTP is a pure function: code = f(shared secret, current time). Enrol the test account once, store the shared secret as a test secret, and compute the current code in the test with a TOTP library: ```ts const code = totp(process.env.E2E_TOTP_SECRET); await page.getByLabel('Authentication code').fill(code); ``` Two operational cautions. Codes rotate on a fixed interval, so a test that generates a code and then spends too long before submitting can submit an expired one — generate it immediately before filling. And the machine's clock matters: significant clock skew on a CI runner makes every generated code invalid, which presents as a mysterious "invalid code" failure that reproduces nowhere else. For an emailed or texted code there is no such function, so the code has to be *read*. Use a mail-testing service or a catch-all test mailbox with an API, poll it for the message addressed to this test's identity, extract the code, and submit it. Give each test its own recipient address so a parallel run cannot consume another test's message. ## Where to draw the line The judgment an interviewer is listening for is proportionality. Authentication is high-stakes — when it breaks, nothing works — so it must have real coverage. But it is also the slowest, most externally-dependent flow in the product, so that coverage should be a handful of tests, possibly on their own schedule (a pre-release or nightly job rather than every pull request), with the main suite fast and injected. Say that split out loud; "test everything through the real flow" and "never test the real flow" are both wrong answers.
- Which part of the login flow can an injected-session test never cover, even indirectly?The authentication guard and the return-to path. Injected tests start already authenticated, so they never hit the redirect to login and never exercise the round trip back to the originally requested URL after signing in. That path breaks easily during routing refactors, which is why it deserves an explicit test in the small real-flow suite.
- What exactly are you no longer testing if you replace the real identity provider with a local mock?The provider itself — its consent screen, its account state, its error responses and its real token contents. You still cover your own half: how you build the authorization request, how you validate state and nonce, how you handle the callback and establish your session. That is often the right trade, but call it out rather than claiming full federated coverage.
- How would you schedule these tests differently from the rest of the suite?Run the fast injected-session suite on every pull request, and put the real-flow tests — especially the federated one — on a pre-merge or nightly job against a stable environment. They are slower and depend on an external system, so gating every commit on them buys little and imports someone else's outages into your pipeline.
saying these in an interview costs you the question
- Deletes login coverage entirely once sessions are injected
- Drives the full third-party OAuth flow in every test
- Says MFA cannot be automated because codes are on a phone
- Generates a TOTP code long before submitting the form
- Treats a mocked provider as full federated login coverage