Bloga döntutorial

SMS OTP Uçtan Uca Testi: CI'da Doğrulamayı Otomatikleştirin

SMS OTP Uçtan Uca Testi: CI'da Doğrulamayı Otomatikleştirin

Çoğu ekip kayıt ve giriş akışlarını titizlikle test eder. Ta ki altyapıdan dışarı çıkan tek adıma gelene kadar: SMS ile gelen tek kullanımlık şifre. Kod, backend'inizden SMS sağlayıcısına, oradan operatör ağına, telefona ve tekrar formunuza uzanan uzun bir yol izler. Test aracının bu yolculuğu takip etmesi zor olduğu için birçok ekip bu adımı sessizce atlar. Sonra bir cuma akşamı yapılan küçük bir konfigürasyon değişikliği OTP gönderimini bozar ve kimse fark etmez. Pazartesi sabahı kayıt grafiği dümdüz olana kadar.

Bu yazıda SMS OTP için CI hattınızda güvenle çalışan uçtan uca testleri nasıl kuracağınızı anlatıyoruz. Mock'un ne zaman yeterli olduğunu, gerçek numaraya ne zaman ihtiyaç duyduğunuzu göreceksiniz. Rastgele patlamayan bir polling yardımcısını nasıl yazacağınızı ve test paketini hem uygun maliyetli hem güvenli tutmanın yollarını da ele alacağız.

SMS OTP Neden Klasik E2E Testlerini Zorlar?

Sıradan bir tarayıcı testi dokunduğu her şeyi kontrol eder. Tıklar, yazar, DOM'u okur ve doğrular. SMS doğrulama ise bu modeli birkaç noktada kırar:

  • Kod başka bir kanaldan gelir. Sayfada hiç görünmez, test onu okumak için ikinci bir kanala muhtaçtır.
  • Gecikme tahmin edilemez. Mesaj iki saniyede de gelebilir, kırk saniyede de. Sabit sleep ya zaman kaybettirir ya da rastgele hata verir.
  • Kodlar tek kullanımlıktır ve süresi dolar. Yeniden denenen test eski kodu kullanamaz, yavaş bir test ise kodun ömrünü aşabilir.
  • Hız limitleri devreye girer. Hem sizin kötüye kullanım kurallarınız hem SMS sağlayıcınızın limitleri, aynı numarayı sürekli yoklayan testi frenler.
  • Ortak numaralar çakışır. Paralel çalışan iki iş aynı telefonu kullanırsa birbirinin kodunu okur.

Bunların hiçbiri OTP'yi test edilemez yapmaz. Sadece mevcut bir teste telefon adımı iliştirmek yerine bilinçli bir strateji gerektiğini gösterir.

Otomatik Testlerde OTP'yi Ele Almanın Üç Yolu

1. SMS sağlayıcısını mock'lamak

Backend'iniz SMS sağlayıcısıyla bir adaptör üzerinden konuşur. Test ortamında bu adaptörü, giden mesajları bellekte ya da bir tabloda saklayan sahte bir sürümle değiştirirsiniz. Test de kodu yalnızca test ortamında açık olan bir endpoint'ten okur.

Hızlıdır, her seferinde aynı sonucu verir ve çalıştırma başına maliyet çıkarmaz. Sorun şu: artık gönderimi test etmiyorsunuz. Süresi dolmuş bir API anahtarı, yanlış gönderici adı ya da operatör filtresine takılan bir şablon, bu testlerden yeşil geçer.

2. Test moduna özel bypass

Bazı ekipler belirli test numaralarını beyaz listeye alır ve bu numaralar staging'de hep aynı sabit kodu kabul eder. UI testlerini sadeleştirir, SMS masrafını tamamen ortadan kaldırır.

Risk, bu bypass mantığının canlı ortama sızmasıdır. Kullanacaksanız, production build'lerde açılamayan bir ortam değişkeninin arkasına koyun ve bu korumanın kendisini de birim testiyle güvenceye alın.

3. Sanal numaralarda gerçek SMS almak

Bu yöntemde test, bir API üzerinden gerçek bir telefon numarası kiralar, onu uygulamanıza girer, gerçek SMS'i bekler, kodu ayıklar ve doğrulamayı tamamlar. Zincirin tamamının çalıştığını kanıtlayan tek yaklaşım budur: sizin kodunuz, SMS sağlayıcınız, operatör yönlendirmesi ve mesaj formatı.

Daha yavaştır ve her çalıştırmanın küçük bir maliyeti vardır. Bu yüzden her teste değil, odaklı bir smoke paketine aittir.

YaklaşımGerçek gönderimi test eder mi?HızÇalıştırma maliyetiEn uygun kullanım
Mock sağlayıcıHayırÇok hızlıYokBirim ve entegrasyon testleri, her PR
Test modu bypassHayırHızlıYokStaging'de UI regresyon testleri
Gerçek sanal numaraEvetDaha yavaşDüşükZamanlanmış smoke testleri, sürüm öncesi kontrol

Telefon Doğrulaması İçin Gerçekçi Bir Test Piramidi

En sağlıklı kurgu, üç katmanı klasik test piramidi mantığıyla bir araya getirir.

SMS OTP doğrulaması için birim, entegrasyon ve uçtan uca katmanlardan oluşan test piramidi

Birim testleri (çok sayıda). Kod üretimi, geçerlilik süresi, deneme sayacı, hesap kilitleme ve tekrar gönderme bekleme süresi gibi kuralları kapsar. Milisaniyeler içinde koşar ve mantık hatalarının büyük kısmını yakalar.

Entegrasyon testleri (bir miktar). SMS adaptörünüzü, sağlayıcının API'sini taklit eden bir mock sunucuya karşı çalıştırır. Geçersiz numara, limit aşımı, yetersiz bakiye gibi hata yanıtlarını da mutlaka dahil edin. Birden fazla sağlayıcıyla çalışıyorsanız sözleşme (contract) testleri burada çok işe yarar.

Uçtan uca testler (az sayıda). Gerçek numarayla birkaç kritik akış: kayıt, iki faktörlü giriş, telefon numarası değiştirme ve kullanıcılarınızın yoğun olduğu bir ülke. Başka hiçbir katmanın yakalayamadığı sorunları bunlar yakalar. Değiştirilmiş bir API anahtarı ya da operatörlerin birden filtrelemeye başladığı bir şablon gibi.

Gerçek Numarayla E2E Testi Kurmak

Akış üç parçadan oluşur: numara almak, kodu tetikleyip yakalamak ve arayüzü sürmek. Örneklerde Playwright ve TypeScript kullandık, ama aynı desen Cypress ya da Selenium'da da birebir çalışır.

Adım 1: Numarayı kod üzerinden kiralayın

Testinizin her çalıştırmada yeni bir numaraya ihtiyacı var. Masaya bantlanmış ortak bir SIM kart değil, API ile alınan bir numara. SMSBulk sanal numaraları gibi bir servis, dünya genelinde HTTP üzerinden sipariş edip okuyabileceğiniz ve serbest bırakabileceğiniz gerçek numaralar sunar. Tam endpoint ve parametreler API dokümantasyonunda yer alıyor. Aşağıdaki yardımcı, orada anlatılan v1 aktivasyon endpoint'lerini kullanır.

Adım 2: Kodu zaman aşımıyla yoklayın

Sabit bekleme kullanmayın. Artan aralıklarla yoklayın, kesin bir üst sınır koyun ve kodu mesaj şablonunuza uyan bir kalıpla doğrulayın.

// tests/helpers/otp.ts
// SMSBulk REST API v1: aktivasyon endpoint'leri.
const API = 'https://smsbulk.net/api/v1';
const KEY = process.env.SMS_API_KEY!;
const headers = { 'x-api-key': KEY, 'Content-Type': 'application/json' };

export async function rentNumber(countryIso: string) {
  const res = await fetch(`${API}/activations`, {
    method: 'POST',
    headers,
    body: JSON.stringify({ serviceCode: 'ot', countryIso }), // ot = Other (Any)
  });
  if (!res.ok) throw new Error(`NUMARA_KIRALANAMADI: ${res.status}`);
  const a = (await res.json()) as { id: string; phoneNumber: string };
  return { id: a.id, phone: `+${a.phoneNumber}` }; // API yalnız rakam döndürür
}

export async function waitForCode(id: string, timeoutMs = 120_000) {
  const started = Date.now();
  let delay = 2_000;
  while (Date.now() - started < timeoutMs) {
    const res = await fetch(`${API}/activations/${id}`, { headers });
    const data = await res.json();
    const code: string | null | undefined = data.smsCode; // status=RECEIVED olana kadar null
    const match = code?.match(/^[0-9]{6}$/); // şablonunuza göre daraltın
    if (match) return match[0];
    await new Promise((r) => setTimeout(r, delay));
    delay = Math.min(delay * 1.5, 10_000);
  }
  throw new Error(`OTP_GELMEDI: ${timeoutMs / 1000} sn doldu`);
}

export async function releaseNumber(id: string) {
  const res = await fetch(`${API}/activations/${id}`, { headers });
  const a = await res.json();
  if (a.status === 'RECEIVED') {
    // Kod geldi: aktivasyonu tamamlandı olarak işaretle.
    await fetch(`${API}/activations/${id}/complete`, { method: 'POST', headers });
  } else if (a.cancellable) {
    // Kod gelmedi: iptal et, bakiye iade edilir.
    await fetch(`${API}/activations/${id}`, { method: 'DELETE', headers });
  }
  // Aksi hâlde 2 dakikalık erken iptal beklemesi sürüyordur (bkz.
  // cancellableAt) ya da aktivasyon zaten son durumdadır. Kullanılmayan
  // numara süresi dolunca otomatik iade edilir.
}

Burada birkaç ayrıntı önemli. Bekleme süresi kısa başlar, böylece hızlı gelen mesajlar hemen yakalanır. Sonra API'yi boğmamak için uzar. Regex'i şablonunuzun izin verdiği kadar dar tutun. Mesajınız "Doğrulama kodunuz: 482913. 5 dakika geçerlidir." gibiyse tam altı haneyi eşlemek, gevşek bir aralıktan daha güvenlidir. Böylece eksik ya da bozuk bir değeri yanlışlıkla kabul etmemiş olursunuz.

Adım 3: Arayüzü sürün

// tests/otp/kayit.spec.ts
import { test, expect } from '@playwright/test';
import { rentNumber, waitForCode, releaseNumber } from '../helpers/otp';

test('yeni kullanıcı SMS OTP ile kayıt olabilir', async ({ page }) => {
  const { id, phone } = await rentNumber('us');
  try {
    await page.goto('/kayit');
    await page.getByLabel('Telefon numarası').fill(phone);
    await page.getByRole('button', { name: 'Kod gönder' }).click();

    const code = await waitForCode(id);
    await page.getByLabel('Doğrulama kodu').fill(code);
    await page.getByRole('button', { name: 'Doğrula' }).click();

    await expect(page.getByText('Hoş geldiniz')).toBeVisible();
  } finally {
    await releaseNumber(id);
  }
});

finally bloğu kritik. Test geçse de kalsa da numara serbest bırakılır, gereğinden uzun süre elde tutulmaz. Numara formatına da dikkat edin. Bizim API'miz yalnızca rakam döndürür, bu yüzden yukarıdaki yardımcı E.164 biçimini (artı işareti, ülke kodu ve abone numarası) oluşturmak için başa artı işareti ekler. Formunuzda ayrı bir ülke seçici varsa numarayı bölmeniz gerekir. Kenar durumlar için E.164 telefon numarası formatı rehberimize göz atabilirsiniz.

Test Paketini CI Hattında Çalıştırmak

Gerçek numaralı testler her commit'te koşmamalı. Makul bir varsayılan şöyle:

  • Her pull request'te: mock sağlayıcıyla birim ve entegrasyon testleri.
  • main'e birleştirmede ya da sürüm öncesinde: staging'e karşı gerçek numaralı smoke paketi.
  • Zamanlanmış olarak: aynı smoke paketi birkaç saatte bir. Gönderim zinciri için sentetik izleme görevi görür.

Zamanlanmış iş için bir GitHub Actions örneği:

name: otp-e2e
on:
  schedule:
    - cron: '0 */6 * * *'
  workflow_dispatch:
concurrency:
  group: otp-e2e
  cancel-in-progress: false
jobs:
  e2e:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npx playwright install --with-deps chromium
      - run: npx playwright test tests/otp --retries=1
        env:
          SMS_API_KEY: ${{ secrets.SMS_API_KEY }}
          BASE_URL: ${{ vars.STAGING_URL }}

Sanal telefon numarasına gelen SMS doğrulama koduna bağlı CI pipeline çalıştırıcısı

İşin çoğunu üç ayar yapar. concurrency grubu, üst üste binen çalıştırmaların hız limitleri için yarışmasını engeller. Secrets, API anahtarını loglardan ve fork'lardan uzak tutar. --retries=1 ise tek bir gecikmeli mesajı tolere eder ama gerçek bir kesintiyi sonsuz denemelerin arkasına saklamaz.

Testleri paralel parçalar hâlinde koşuyorsanız her worker'a kendi numarasını verin. Tek numarayı işler arasında paylaşmak, bir öğleden sonrayı başka bir teste ait kodların peşinde geçirmek demektir.

OTP Testlerini Kararlı Tutmak

Rastgele patlayan OTP testleri, hiç olmayan testten beterdir. Çünkü ekip bir süre sonra kırmızı build'i görmezden gelmeyi öğrenir. Sinyali temiz tutan alışkanlıklar şunlar:

  • Hata nedenlerini ayırın. "Numara kiralanamadı", "OTP gelmedi" ve "UI doğrulaması başarısız" için farklı hata mesajları fırlatın. Gece üçte build kırıldığında mesaj size uygulamaya mı, SMS sağlayıcısına mı, yoksa testin kendisine mi bakacağınızı söylemeli.
  • Tahmin değil, tavan belirleyin. Artan bekleme ile iki dakikalık zaman aşımı çoğu rota için makul. Mesajlar düzenli olarak daha geç geliyorsa bu, ayarla gizlenecek bir şey değil, incelenecek bir bulgudur.
  • Test kimlikleri için kendi limitlerinizi gevşetin. Staging'de test çalıştırıcısını beyaz listeye alın ki kötüye kullanım kuralları meşru çalıştırmaları engellemesin. Canlı ortam kurallarına dokunmayın.
  • Başarısızlıkta numarayı değiştirin. Bir numaraya kod hiç gelmiyorsa onu bırakın ve hata vermeden önce bir kez yeni numarayla deneyin. Bazı numaralar belirli göndericiler tarafından filtrelenebilir.
  • Failover senaryonuzu test edin. Backend'iniz bir sağlayıcı çöktüğünde diğerine geçiyorsa yedek yolu zorla devreye sokan bir test ekleyin. Sağlayıcı failover rehberimizdeki desenler doğrudan test senaryosuna dönüştürülebilir.

Hata gerçekten bir gönderim sorunu gibi görünüyorsa testi suçlamadan önce OTP kodunun neden gelmediğine dair yaygın nedenleri gözden geçirin.

Güvenlik ve Maliyet Disiplini

Otomatik OTP testleri gerçek kimlik bilgilerine ve gerçek paraya dokunur. Onlara canlı ortam kodu gibi davranın.

  • Kodları ve numaraları loglarda tam hâliyle göstermeyin. Test çıktısında ve trace'lerde maskeleyin. CI logları çoğu zaman sanıldığından çok daha geniş bir kitleye açıktır.
  • API anahtarlarını CI secret olarak saklayın ve yalnızca ihtiyaç duyan repo ile ortama tanımlayın. Belirli aralıklarla yenileyin.
  • Harcamayı sınırlayın. Test için ayrı bir hesap ya da bakiye kullanın, sabit bir tutar yükleyin ve düşük bakiye uyarısı kurun. Kontrolden çıkan bir döngü ana hesabınızı değil, küçük bir test bütçesini tüketsin.
  • Sadece size ait olanı test edin. Bu paketleri kendi uygulamanıza yöneltin. Üçüncü taraf platformlarda kayıtları otomatikleştirmek onların kullanım şartlarını ihlal eder ve bu kurgunun amacı bu değil.
  • Bypass kodlarını canlıdan uzak tutun. Herhangi bir yerde test modu kısayolu kullanıyorsanız, bu bayrak açıkken deploy'u durduran bir kontrol ekleyin.

Sık Sorulan Sorular

SMS OTP uçtan uca testleri her pull request'te çalışmalı mı?

Genellikle hayır. Mock'lu testleri her PR'da, gerçek numaralı testleri ise birleştirme, sürüm ve zamanlanmış kontrollerde çalıştırın. Kapsam, hız ve maliyet arasındaki dengeyi böyle kurarsınız.

Polling zaman aşımı ne kadar olmalı?

Artan bekleme aralığıyla yaklaşık iki dakikadan başlayın. Gerçekte gözlemlediğiniz gönderim sürelerine göre ayarlayın, sürekli yavaş gönderimi de araştırılması gereken bir hata olarak görün.

Test için kendi telefonumu kullanamaz mıyım?

Manuel kontroller için olur, ama CI'da ölçeklenmez. Fiziksel bir SIM paralel işler arasında paylaşılamaz, birinin mesajı okuması gerekir ve tek bir arıza noktasına dönüşür.

Gerçek numarayla test ediyorsam mock'a yine ihtiyacım var mı?

Evet. Mock her değişiklikte hızlı ve tutarlı geri bildirim verir, gerçek numara ise gönderim zincirini doğrular. İkisine de ihtiyacınız var.

SMSBulk ile Başlayın

Telefon doğrulama akışınızın henüz hiç otomatik testi yoksa küçük başlayın: bir kayıt testi, bir gerçek numara, bir zamanlanmış iş. SMSBulk hesabı açın, test için ayrı bir bakiye ayırın ve API dokümantasyonundaki numara kiralama ile SMS okuma çağrılarını test çalıştırıcınıza bağlayın. Bir öğleden sonra içinde, OTP gönderimi bozulduğunda bunu kullanıcılarınızdan önce size haber veren bir CI kontrolünüz olur.

#e2e testing#sms otp#ci/cd#phone verification#test automation

Hesaplarınızı kolayca doğrulamaya hazır mısınız?

190+ ülkeden 30 saniyenin altında anlık SMS kodları alın.

İlgili Makaleler