How to Test SMS OTP Verification Without a Real Phone Number (Developer Guide)

Learn how to test SMS OTP flows end-to-end without a SIM card. Covers virtual number APIs, mocking, Twilio test creds, and a working Python test example.

NNanami
June 6, 20267 min read

You have built an SMS OTP flow into your app. Now you need to test it — but burning your personal SIM for every test run is slow, messy, and breaks as soon as your carrier throttles your messages. Using a colleague's number gets complicated. And public shared numbers get blacklisted.

This guide covers every practical method for testing SMS OTP verification during development, with honest trade-offs for each approach. Whether you are building a one-off project or shipping a production OTP flow, there is a method here that fits.

Method 1: Virtual Phone Numbers via API (Best for CI/CD and Integration Testing)

A virtual phone number service gives you a real phone number that can receive SMS, accessible via a dashboard or REST API. Unlike mock testing, you are actually hitting your SMS provider's real delivery pipeline — which means you catch real-world issues: delivery delays, encoding bugs, carrier filtering, and message formatting problems.

How it works for developers:

  1. Request a number via the NumberOTP API: POST /v1/activations with service and country
  2. Your app sends the OTP SMS to the returned phone number
  3. Poll GET /v1/activations/{id} until status: received
  4. Read the OTP from the otp field in the response
  5. Submit the OTP to your app to complete verification
  6. Assert the expected outcome in your test

This is a fully automated, end-to-end OTP test that exercises your real SMS sending code, your delivery provider, and your verification logic — without touching a physical SIM card.

Numbers are available in 150+ countries, cost under $0.10 each, and the API returns results in seconds. You can run dozens of test cases per day for less than a dollar.

Example curl workflow:

# 1. Get a number
curl -X POST https://numberotp.com/api/v1/activations \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -d '{"service": "google", "country": "us"}'
# Response: {"id": "abc123", "phone_number": "+14155550123", "status": "waiting"}

# 2. Your app sends OTP to +14155550123

# 3. Poll for the code
curl https://numberotp.com/api/v1/activations/abc123 \
  -H 'Authorization: Bearer YOUR_API_KEY'
# Response: {"status": "received", "otp": "847291"}

Method 2: Mocking at the Service Layer (Best for Unit Tests)

For unit tests that should run fast and offline, mock your SMS sending function rather than hitting a real delivery provider. This is standard practice but has an important limitation: you are testing your code, not the integration.

Example in Jest (Node.js):

jest.mock('./smsService', () => ({
  sendOtp: jest.fn().mockResolvedValue({ messageId: 'mock-123', status: 'sent' })
}));

test('OTP is sent on registration', async () => {
  await registerUser({ phone: '+14155550123' });
  expect(smsService.sendOtp).toHaveBeenCalledWith('+14155550123', expect.any(String));
});

Mocking works well for: checking that your code calls the SMS service with the right parameters, testing error handling when SMS delivery fails, and keeping unit tests fast and deterministic.

It does not work for: confirming real delivery, testing carrier-specific behaviour, or validating that your OTP message is correctly formatted and delivered.

Method 3: Hardcoded Test OTPs in Staging (Common but Risky)

Many teams configure their staging environment to accept a magic OTP code (e.g. 000000 or 123456) for any phone number, bypassing real SMS delivery entirely.

This speeds up manual testing but has serious risks if the configuration ever leaks to production, and it does not test the actual SMS delivery path at all. It also fails for end-to-end test suites that need to validate the full flow including message delivery.

If you use hardcoded test codes, always gate them behind an explicit environment variable that is unset in production, and add a CI check to verify it is never deployed.

Method 4: Twilio / AWS SNS Test Numbers

Twilio provides test credentials that accept requests but do not actually send SMS. The response looks like a real API response, but no message is delivered. AWS SNS has a similar sandbox mode.

This is useful for testing that your API calls are correctly formatted, but it has the same limitation as mocking: nothing actually gets delivered, so you are not testing end-to-end delivery.

Use Twilio test credentials for validating your API integration code. Use real virtual numbers (Method 1) when you need to confirm actual delivery.

Method 5: Dedicated Test Phone Numbers (Manual, Expensive)

Some teams buy a dedicated SIM card used only for testing. This is fine if you have one, but it creates bottlenecks: only one test can use the number at a time, codes from previous tests can interfere, and you need physical access to the device.

For any team running automated tests, a virtual number API is strictly better: parallelisable, accessible from any machine, and far cheaper per use.

Choosing the Right Method for Each Testing Layer

Test TypeRecommended MethodWhy
Unit testsMock SMS serviceFast, deterministic, no external calls
Integration testsVirtual number APITests real delivery pipeline end-to-end
E2E / acceptance testsVirtual number APIFull user journey including SMS receipt
Manual QAVirtual number dashboard or magic codesSpeed of manual testing
Load testing SMSVirtual number API (bulk)Parallel number acquisition at scale

Setting Up Automated OTP Testing with NumberOTP

Here is a minimal Python example that tests a full OTP registration flow end-to-end:

import requests, time

API_KEY = 'your_numberotp_api_key'
BASE = 'https://numberotp.com/api/v1'

def test_otp_registration():
    # 1. Get a virtual number
    r = requests.post(f'{BASE}/activations',
        headers={'Authorization': f'Bearer {API_KEY}'},
        json={'service': 'your_service', 'country': 'us'})
    act = r.json()
    phone = act['phone_number']
    act_id = act['id']

    # 2. Trigger your app to send OTP to this number
    requests.post('https://your-app.com/api/register',
        json={'phone': phone})

    # 3. Poll for the code (max 60s)
    for _ in range(12):
        time.sleep(5)
        status = requests.get(f'{BASE}/activations/{act_id}',
            headers={'Authorization': f'Bearer {API_KEY}'}).json()
        if status.get('status') == 'received':
            otp = status['otp']
            break
    else:
        raise AssertionError('OTP not received within 60 seconds')

    # 4. Submit OTP to complete verification
    result = requests.post('https://your-app.com/api/verify',
        json={'phone': phone, 'otp': otp})
    assert result.json()['verified'] == True
    print(f'Test passed — OTP {otp} verified successfully')

This pattern works in any CI/CD pipeline. You can parameterise the country and service to run the same test across multiple regions in parallel.

Get your API key at numberotp.com/docs.

Common Pitfalls When Testing OTP Flows

  • Rate limiting on your SMS provider — Run tests with delays between cases, or use separate virtual numbers for parallel test cases to avoid provider throttling.
  • OTP expiry — Virtual numbers deliver codes within seconds, but if your test poll interval is too long, the OTP may expire before you submit it. Set expiry to at least 5 minutes in your test environment.
  • Number reuse between tests — Always request a fresh number for each test case to avoid OTP collisions from previous runs.
  • Carrier filtering in certain countries — If an SMS is not delivered, try a different country. Carrier filtering behaviour varies. NumberOTP routes through providers optimised for A2P delivery.
  • Using VoIP numbers — Services like WhatsApp and Google block VoIP numbers at the verification step. Use carrier-grade virtual numbers, not Google Voice or Twilio, when testing verification for these platforms.

Frequently Asked Questions

How to test OTP SMS?

The most reliable method is using a virtual phone number API. Request a real number programmatically, trigger your app to send the OTP, then poll the API to read the received code. This tests your entire SMS delivery pipeline end-to-end, not just the code that calls the SMS service.

What app lets you receive SMS verification codes for free?

NumberOTP gives you $0.10 in free credits on signup — enough for several test activations. For ongoing testing, numbers cost under $0.10 each. Free public shared number sites do not work for most platforms because they are blacklisted by WhatsApp, Google, and others.

How to get OTP without a SIM card?

Use a virtual phone number. You get a real number that receives SMS without any SIM card or hardware. Access the number through a web dashboard or via the REST API for automated testing.

Can I test WhatsApp OTP in development without a real SIM?

Yes — use a carrier-grade virtual number (not VoIP). NumberOTP provides numbers on real carrier networks that WhatsApp accepts. VoIP numbers from Google Voice or Twilio will be rejected by WhatsApp at the verification step.

Final Thoughts

A robust OTP testing strategy layers mock tests (for speed and unit coverage) with real virtual number tests (for end-to-end confidence). The virtual number layer catches the class of bugs that mocks never will: actual delivery failures, carrier filtering, message encoding issues, and timing edge cases.

The cost is negligible — a full automated test suite running dozens of OTP flows per day costs less than a cup of coffee per month. The confidence it buys is worth far more.

Ready to build it? Read the NumberOTP API docs and get your first test running in under 10 minutes. Sign up free →

Frequently Asked Questions

How to test OTP SMS?+

The most reliable method is using a virtual phone number API. Request a real number programmatically, trigger your app to send the OTP, then poll the API to read the received code. This tests your entire SMS delivery pipeline end-to-end, not just the code that calls the SMS service.

What app lets you receive SMS verification codes for free?+

NumberOTP gives you $0.10 in free credits on signup — enough for several test activations. For ongoing testing, numbers cost under $0.10 each. Free public shared number sites do not work for most platforms because they are blacklisted by WhatsApp, Google, and others.

How to get OTP without a SIM card?+

Use a virtual phone number. You get a real number that receives SMS without any SIM card or hardware. Access the number through a web dashboard or via the REST API for automated testing.

Can I test WhatsApp OTP in development without a real SIM?+

Yes — use a carrier-grade virtual number (not VoIP). NumberOTP provides numbers on real carrier networks that WhatsApp accepts. VoIP numbers from Google Voice or Twilio will be rejected by WhatsApp at the verification step.

Written by

N

Nanami

Nanami is a telecom and digital privacy specialist at NumberOTP with over 8 years of experience in SMS verification systems, virtual phone infrastructure, and online identity protection. He covers OTP security, number masking, developer APIs, and privacy-first verification workflows for businesses and developers worldwide.