Every app with phone verification faces the same question in its test suite: do we mock the SMS provider or send real messages? The answer is "both, in different places". Here's the split that has worked for us.
Mock in unit and integration tests
Most tests don't care whether an SMS really left the building. They care that your code:
- generates a code with the right length and expiry,
- stores it hashed, not in plain text,
- rate-limits requests per number and per IP,
- invalidates old codes correctly,
- calls the provider with the right parameters.
For all of that, inject a fake SMS client that records what it was asked to send. It's fast, free and deterministic. Run it on every commit.
class FakeSms:
def __init__(self):
self.sent = []
def send(self, to, text):
self.sent.append((to, text))
def test_code_is_sent(app):
sms = FakeSms()
app.sms = sms
app.request_code("+447700900123")
assert sms.sent[0][0] == "+447700900123"
Use real SMS for delivery checks
Mocks can't tell you that:
- a country started rejecting your sender ID,
- your provider's route to one carrier degraded,
- a template change pushed the message into UCS-2 and two parts,
- codes take 90 seconds to arrive in one region.
Those only show up with real messages to real numbers. Run a small set of end-to-end checks on a schedule (nightly, or hourly for critical markets) against staging, using online numbers in the countries you care about. Log delivery time and the sender ID you received, and alert when they change.
I run Receive SMS, which has an API for this: buy a number for a country, trigger your sign-up, poll for the SMS, and you're only charged if it arrives. Any provider with a similar API works the same way.
The split in one table
| Test type | SMS | Runs | Checks |
|---|---|---|---|
| Unit / integration | Fake client | Every commit | Logic, limits, storage |
| E2E delivery | Real numbers | Nightly / hourly | Delivery, speed, sender ID, text |
Keep the real-SMS suite small. A handful of countries and one happy path each is usually enough to catch the problems mocks can't see.
Top comments (0)