Real SMS or mocked verification: choose the test for the question
A fixed code checks your application quickly. A real message checks the delivery journey too. Use this coverage map to decide which test belongs in a pull request, staging check, or release gate.
Match coverage to the failure you want to catch
Start with the question behind the test. “Does this form explain an invalid code?” and “Does our staging sender reach a real receiving number?” need different evidence.
| Test layer | Good evidence for | What remains outside it |
|---|---|---|
| Mocked response in a component or browser test | Input rules, loading state, error text, navigation | Sender configuration, delivery and real message format |
| Provider's fictional number / fixed OTP mode | Application integration with that provider's test mode | Actual sending and receipt |
| Real receiving number | Sending path, receipt, extraction, final verification | Every other destination, region or carrier |
Scroll horizontally to see all columns.
Use the cheapest layer that answers the question. A deterministic invalid-code test is valuable even when you also have a real-delivery acceptance test.
Define the real test's finish line
Receiving a text is one checkpoint. The full test also submits its code or link and asserts the application's final state. For sign-in, that might be an authenticated account page. For a phone change, it might be the new verified number in account settings.
Record the inbox position before requesting the message. Use a destination outside any fictional-number mapping. Check that the application is using the sender configuration and environment you intend to cover.
Firebase's documentation describes fictional phone numbers and fixed codes for development. Keep those useful tests, then give real delivery its own destination and test identity.
Place each test where its result is useful
| When | Suggested coverage | Owner of a failure |
|---|---|---|
| Every change to the form | Fast mocked success and error states | Application developer |
| Auth integration change | Provider test-mode flow | Auth integration owner |
| Sender configuration change | One real verification journey | Application and delivery owner |
| Release or scheduled staging check | Real journey in the environment being checked | Release / QA owner |
Scroll horizontally to see all columns.
Choose cadence from your release risk and delivery cost. A separate real-SMS test lets the team identify an external delivery issue without obscuring an unrelated form regression.
Add one real journey, then expand intentionally
- Choose an existing sign-in account or a resettable registration identity.
- Assign an independent receiving number and a selected-number credential.
- Mark the inbox, request delivery through the app, and wait within a deadline.
- Submit the received value and assert the final state.
- Classify failure as setup, send, receive, extract or verify.
The Playwright recipe implements this journey. Once it runs locally, use the CI workflow to carry the same test into GitHub Actions.
Questions you may have
Should we remove mocked OTP tests?
Keep them for the states they cover well: malformed input, expired-code messages, resend controls and loading behavior. Add real receipt for delivery confidence. Changing the test layer should follow a coverage gap, rather than a desire to make every test use SMS.
What does a passing real-SMS test prove?
It proves that the tested account, destination, sender configuration and verification step worked in that run. Keep those details with the test result so later failures are comparable. It does not establish universal compatibility with every service or carrier.
Can a provider sandbox prove delivery?
A sandbox or fixed-code mode can prove its documented integration behavior. To establish actual receipt, use a real destination and the provider mode that sends an SMS. Check the provider recipe for the environment-specific setup.