How to Test SMS Verification Flows in CI/CD Pipelines
Practical strategies for automating SMS verification tests in your continuous integration and deployment workflows.
Automated testing is a cornerstone of modern software development, but testing SMS verification flows presents unique challenges. You cannot easily receive a real text message inside a CI runner. Here are practical approaches to solve this problem.
The Challenge
SMS verification is inherently an out-of-band process: the code is delivered through a channel (the cellular network) that is separate from your application. This makes it difficult to automate in the way you would automate a simple API call or UI interaction.
Common problems include:
- CI/CD environments do not have phone numbers
- Real SMS delivery is slow, unreliable, and costs money
- Rate limits on SMS providers can cause flaky tests
- Hardcoded test numbers may stop working without warning
Strategy 1: Use a Bypass Code in Staging
The simplest approach is to configure your staging environment to accept a known test code (e.g., "000000") for specific test phone numbers. Your production environment continues to use real verification, while your CI tests use the bypass.
**Pros:** Fast, free, deterministic.
**Cons:** Does not test the actual SMS delivery path. Risk of accidentally enabling bypass in production.
Implementation Tips
- Use environment variables to control whether bypass is active
- Restrict bypass to a specific set of test phone numbers
- Log every bypass usage for audit purposes
- Never deploy bypass logic to production
Strategy 2: Use a Webhook-Based SMS Provider
Some SMS providers (like Twilio) support webhooks that notify your application when a message is sent. In your test environment, you can configure the provider to route messages to an HTTP endpoint that your test suite monitors.
**Pros:** Tests the real SMS sending path. Works with any CI/CD system.
**Cons:** Requires an SMS provider account. Adds latency. Costs money per message.
Implementation Tips
- Use a dedicated test account with its own quota
- Set up a lightweight server that captures incoming messages
- Use polling or WebSockets to wait for the message in your test
- Set a reasonable timeout (15-30 seconds) to avoid hanging tests
Strategy 3: Use Temporary Phone Number Services
Services that provide temporary phone numbers with an API allow you to request a number, trigger your verification flow, and then poll for the incoming message. This tests the real SMS path without requiring your own phone number infrastructure.
**Pros:** Tests end-to-end SMS delivery. No hardware required.
**Cons:** Shared numbers may receive unrelated messages. Reliability depends on the service.
Implementation Tips
- Filter incoming messages by sender or content pattern
- Use a fresh number for each test run if possible
- Implement retry logic for message retrieval
- Keep test timeouts generous to account for carrier delays
Strategy 4: Mock the SMS Provider
Replace the SMS provider with a mock in your test environment. Instead of actually sending an SMS, the mock stores the code in memory or a database. Your test retrieves the code directly from the mock.
**Pros:** Fast, free, fully deterministic. No external dependencies.
**Cons:** Does not verify that the SMS provider integration works correctly.
Implementation Tips
- Use dependency injection to swap the real provider for the mock
- Store sent codes in a simple in-memory map keyed by phone number
- Expose an API endpoint on the mock to retrieve the latest code
- Run a separate integration test suite with the real provider on a less frequent schedule
Recommended Approach
For most teams, we recommend a layered strategy:
1. **Unit tests** — Mock the SMS provider. Run on every commit.
2. **Integration tests** — Use a webhook-based provider or temporary numbers. Run daily or on pull requests.
3. **End-to-end tests** — Test with real numbers in a staging environment. Run before major releases.
This gives you fast feedback on every change while still validating the complete SMS path on a regular basis.