Pe această pagină
De ce strică OTP automatizarea
Un test poate da clic, tasta și verifica, dar nu poate citi un telefon. Fiecare dintre soluțiile ocolitoare obișnuite sacrifică ceva:
- Dezactivarea 2FA în staging înseamnă că fluxul de autentificare pe care îl folosesc de fapt utilizatorii nu este testat niciodată.
- Codurile de test fixe funcționează doar în sistemele pe care le controlezi și ascund problemele de livrare.
- O persoană cu telefonul nu ține pasul cu o rulare de noapte și nu e niciodată acolo la 3 dimineața.
Abordarea: un număr real, citit prin API
Pune un SIM al tău într-un telefon Android (sau folosește un iPhone cu o automatizare din Comenzi rapide), instalează BusySMS și verifică numărul. Înregistrează contul de test pe acel număr. În test, declanșează SMS-ul, cere API-ului BusySMS următorul cod care sosește și introdu-l. Codul este real, calea de livrare este reală și nimeni nu trebuie să stea cu ochii pe telefon.
- Fără coduri vechi. O cerere în așteptare returnează doar un cod sosit după ce a început, așa că un test nu trimite niciodată codul din rularea anterioară.
- O singură cerere.
GET /api/v1/codes?timeoutMs=60000face long-polling, deci nu trebuie să scrii nicio buclă de interogare. - Filtre.
phone,serviceșideviceIdîmpiedică testele paralele să-și ia codurile unul altuia.
Configurare inițială
- 1
Înrolează un telefon
Instalează BusySMS din Google Play pe un telefon cu SIM-ul de test, autentifică-te și verifică numărul. Ghidul despre gateway-ul SMS pe Android are detaliile.
- 2
Înregistrează contul de test pe acel număr
Folosește numărul în produsul pe care îl testezi, exact cum ar face un utilizator.
- 3
Creează o cheie API
Panou → API REST → Creează cheie. Copiaz-o imediat: este afișată o singură dată.
- 4
Păstreaz-o ca secret CI
Expune-o runnerului de teste ca
BUSYSMS_API_KEY. Nu o pune niciodată într-un commit și nu o trimite niciodată browserului testat.
O mică funcție ajutătoare
Fiecare exemplu de mai jos folosește aceeași idee: începi așteptarea, declanșezi SMS-ul, iei codul. Pune așteptarea într-o funcție ajutătoare, în TypeScript sau Python:
const API = "https://busysms.net/api/v1/codes";
export async function waitForSmsCode(
opts: { phone?: string; service?: string; timeoutMs?: number } = {},
): Promise<string> {
const params = new URLSearchParams({ timeoutMs: String(opts.timeoutMs ?? 60_000) });
if (opts.phone) params.set("phone", opts.phone);
if (opts.service) params.set("service", opts.service);
const res = await fetch(`${API}?${params}`, {
headers: { Authorization: `Bearer ${process.env.BUSYSMS_API_KEY}` },
});
if (!res.ok) throw new Error(`BusySMS ${res.status}: ${await res.text()}`);
const { code } = (await res.json()) as { code: string | null };
if (!code) throw new Error("No new SMS code arrived in time");
return code;
}import os
import requests
API = "https://busysms.net/api/v1/codes"
def wait_for_sms_code(phone=None, service=None, timeout_ms=60_000):
params = {"timeoutMs": timeout_ms}
if phone:
params["phone"] = phone
if service:
params["service"] = service
r = requests.get(
API,
params=params,
headers={"Authorization": f"Bearer {os.environ['BUSYSMS_API_KEY']}"},
timeout=timeout_ms / 1000 + 15,
)
r.raise_for_status()
code = r.json()["code"]
if not code:
raise TimeoutError("No new SMS code arrived in time")
return codePornește așteptarea înainte să dai clic pe Trimite codul. Astfel elimini situația în care un SMS rapid sosește înainte să începi să aștepți. Un cod sosit cu până la aproximativ o secundă înainte de cerere este tratat tot ca nou, dar unul mai vechi nu.
Playwright
import { test, expect } from "@playwright/test";
import { waitForSmsCode } from "./busysms";
test("signs in with SMS two-factor", async ({ page }) => {
test.setTimeout(120_000); // the default 30 s is shorter than an SMS wait
await page.goto("https://staging.example.com/login");
await page.getByLabel("Phone number").fill("+44 7700 900123");
// Start waiting BEFORE the SMS is sent, then trigger it.
const code = waitForSmsCode({ phone: "447700900123" });
await page.getByRole("button", { name: "Send code" }).click();
await page.getByLabel("Verification code").fill(await code);
await page.getByRole("button", { name: "Verify" }).click();
await expect(page).toHaveURL(/dashboard/);
});Selenium (Python)
Apelurile Selenium sunt blocante, așa că rulează așteptarea pe un fir de execuție separat, declanșează SMS-ul, apoi preia rezultatul:
from concurrent.futures import ThreadPoolExecutor
from selenium import webdriver
from selenium.webdriver.common.by import By
from busysms import wait_for_sms_code
driver = webdriver.Chrome()
driver.get("https://staging.example.com/login")
driver.find_element(By.NAME, "phone").send_keys("+44 7700 900123")
with ThreadPoolExecutor(max_workers=1) as pool:
# Start waiting first, then trigger the SMS.
pending = pool.submit(wait_for_sms_code, phone="447700900123")
driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()
code = pending.result()
driver.find_element(By.NAME, "otp").send_keys(code)
driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()Cypress
Comenzile Cypress rulează în browser, așa că ține cheia departe de el: apelează API-ul dintr-un task Node în cypress.config.ts și folosește rezultatul în test.
import { defineConfig } from "cypress";
export default defineConfig({
e2e: {
setupNodeEvents(on) {
on("task", {
// Runs in Node, so the API key never reaches the browser.
async waitForSmsCode({ phone }: { phone: string }) {
const params = new URLSearchParams({ phone, timeoutMs: "60000" });
const res = await fetch(`https://busysms.net/api/v1/codes?${params}`, {
headers: { Authorization: `Bearer ${process.env.BUSYSMS_API_KEY}` },
});
if (!res.ok) throw new Error(`BusySMS ${res.status}`);
const { code } = await res.json();
return code; // null if nothing new arrived
},
});
},
},
});it("signs in with SMS two-factor", () => {
cy.visit("https://staging.example.com/login");
cy.get("input[name=phone]").type("+44 7700 900123");
cy.contains("button", "Send code").click();
cy.task("waitForSmsCode", { phone: "447700900123" }, { timeout: 90_000 }).then((code) => {
expect(code, "SMS code").to.be.a("string");
cy.get("input[name=otp]").type(String(code));
});
cy.contains("button", "Verify").click();
});Cypress rulează comenzile în ordine, așa că aici așteptarea începe imediat după clic. Livrarea SMS-urilor este rareori atât de rapidă, iar un cod sosit cu până la o secundă înainte de cerere contează în continuare.
Rulări paralele și livrare instabilă
- Un număr per worker paralel este cea mai simplă regulă. Dacă workerii trebuie să împartă un număr, filtrează după
serviceși rulează pe rând autentificările în același serviciu. - O așteptare are o limită de 90 de secunde. Dacă un SMS poate întârzia mai mult, reia așteptarea o dată în loc să mărești timeoutul.
- Când un test primește
null, verifică telefonul înainte să dai vina pe produs:GET /api/v1/numbersraporteazăcanReceiveși unreason, cum ar figateway_offsauphone_unreachable. - Dacă nimic nu capturează când începe o așteptare, BusySMS trimite telefonului o notificare prin care cere repornirea gateway-ului. Cineva trebuie să o atingă.
- Mesajele capturate sunt păstrate cel mult 7 zile, suficient cât să depanezi rularea eșuată de azi-noapte.
Scripturi simple și joburi CI
Nimic de aici nu ține de un anumit framework de testare. Un pas shell într-un job CI poate face același lucru cu curl și jq: pornește așteptarea în fundal, declanșează SMS-ul, apoi citește rezultatul.
# Start waiting in the background, then trigger the SMS
curl -s -H "Authorization: Bearer $BUSYSMS_API_KEY" \
"https://busysms.net/api/v1/codes?phone=447700900123&timeoutMs=60000" > code.json &
WAIT_PID=$!
./trigger-login.sh # whatever makes the service send the SMS
wait "$WAIT_PID"
CODE=$(jq -r .code code.json)
[ "$CODE" != "null" ] || { echo "No SMS code arrived" >&2; exit 1; }
echo "Got code $CODE"jq afișează null când nu a sosit niciun cod, așa că verificarea face jobul să eșueze în loc să scrie cuvântul null într-un formular.
Folosește-l pe conturi care îți aparțin
Funcționează pentru că codul este trimis pe un număr care îți aparține. Folosește-l pentru conturi pe care le controlezi: utilizatorii de test ai produsului tău, propriile autentificări de administrator, mediul tău de staging. Controlezi browserul cu un agent AI în loc de un runner de teste? Ghidul MCP acoperă asta.
BusySMS citește codurile trimise pe propriile tale numere. Nu este o metodă de a ocoli verificarea altcuiva și nu este făcut pentru crearea de conturi în masă.
Întrebări
Întrebări frecvente
Cum automatizez verificarea OTP în Selenium?
Pornește GET /api/v1/codes?timeoutMs=60000 pe un fir de execuție separat, declanșează SMS-ul, apoi introdu codul returnat în câmp. Exemplul Python de mai sus face exact asta.
Pot automatiza OTP fără un telefon real?
Nu cu BusySMS. Codurile vin de la un SIM al tău dintr-un telefon real; emulatoarele nu sunt acceptate ca gateway.
Poate un test să preia vreodată un cod vechi?
Nu. Cu timeoutMs mai mare decât zero, API-ul returnează doar codurile sosite după începerea cererii și returnează null dacă nu a sosit niciunul.
Cât costă rularea în CI?
$5 per număr verificat pe lună după 7 zile gratuite. Prețul este per număr, nu per rulare de test sau per cod.
Funcționează și cu coduri prin e-mail sau cu aplicații de autentificare?
Nu. BusySMS citește codurile trimise prin SMS pe propriile tale numere. Codurile prin e-mail și cele din aplicațiile de autentificare nu sunt SMS-uri, așa că nu sunt acoperite.
Poate toată echipa mea să folosească un singur număr de test?
Da. Partajează numărul cu până la cinci alți utilizatori BusySMS; după ce acceptă, propriile lor chei API primesc și ele codurile noi de pe el.
Blog
Mai multe de pe blog
Toate articoleleCitește mai departe
Ghiduri similare
Toate ghidurileAPI de gateway SMS
Referința API-ului de recepție: autentificare, endpointuri, formatul răspunsului, erori și cod în curl, Node.js și Python.
Citește ghidulCoduri SMS pentru agenți AI
Lasă-l pe Claude, pe Codex sau orice client MCP să aștepte coduri de verificare SMS prin serverul MCP BusySMS.
Citește ghidulGateway SMS pe Android
Transformă un telefon Android al tău într-un gateway ale cărui coduri de verificare le citești prin API, MCP, panou sau Chrome.
Citește ghidulÎncearcă pe un număr care îți aparține
Instalează aplicația, verifică-ți numărul și citește-ți primul cod chiar azi. Fiecare cont începe cu 7 zile gratuite; după aceea costă $5 per număr verificat pe lună și poți anula oricând.
