Guides / Cypress

How to test SMS verification codes in Cypress

Cypress commands run in the browser, which can't wait on an SMS. The fix is to read the code in Node with a task, from an inbox your test environment sends SMS to.

Updated October 8, 20266 min read
Short answer

Route your app's SMS to a mock inbox in the test environment. Register a cy.task in setupNodeEvents that polls the inbox for a code sent to a given number after a given time. In the spec, use a unique number, trigger the SMS, then cy.task("waitForCode", { phone, since }) and type the result.

Why a task, not cy.request

cy.request can call the inbox API, but polling until a code arrives means writing retry logic with recursion. A Node task can simply await a promise that resolves when the code is there, and Cypress waits for it with its normal task timeout.

Step 1: route SMS to the mock in the test environment

Only the environment that runs your app under test changes. Point your SMS provider's SDK at otpmock when OTPMOCK_URL is set: Twilio, Vonage, Telnyx, AWS SNS, AWS End User Messaging, Infobip, Sinch, Bird, Plivo, Telesign, Bandwidth, Netgsm, İleti Merkezi, Verimor and Mutlucell each need one option (setup per provider). For anything else, send to POST /v1/messages/send from a test-only branch.

Step 2: register the task

Download the JavaScript helper with curl -O https://otpmock.com/sdk/otpmock.mjs.

cypress.config.js
import { defineConfig } from "cypress";
import { OtpMock } from "./otpmock.mjs";

const otp = new OtpMock({ baseUrl: process.env.OTPMOCK_URL, apiKey: process.env.OTPMOCK_API_KEY });

export default defineConfig({
  e2e: {
    taskTimeout: 30000,
    setupNodeEvents(on) {
      on("task", {
        waitForCode: ({ phone, since }) => otp.waitForCode(phone, { since }).then((r) => r.code),
      });
    },
  },
});

Step 3: the spec

cypress/e2e/signup.cy.js
it("signs up with a phone number", () => {
  const phone = "+1555" + Cypress._.random(1e7, 9e7); // unique per test
  cy.visit("/signup");
  cy.get("[name=phone]").type(phone);

  const since = Date.now() - 5000;
  cy.contains("Send code").click();

  cy.task("waitForCode", { phone, since }).then((code) => {
    cy.get("[name=code]").type(code);
  });
  cy.contains("Welcome").should("be.visible");
});

Tips

FAQ

Can I call the inbox from the browser instead?

You can, with cy.request and the API key, but then the key is exposed to the page under test and you have to write the polling yourself. A Node task keeps the key out of the browser.

What if my app uses a Verify API (Twilio Verify, Vonage Verify)?

It works the same way. otpmock generates the code for Verify requests and puts it in the inbox, so the task reads it like any other SMS.

Try it on your own suite

The free plan includes 100 messages a month. Using an AI coding assistant? Point it at otpmock.com/llms-full.txt.

Get a free API key

Related guides