Email ảo để test đăng ký: chọn loại nào cho QA và developer
Không phải bài test nào cũng cần email thật. Test luồng đăng ký trên app của chính bạn ở môi trường staging thì một máy chủ thư giả (loại công cụ "bắt thư" chạy nội bộ) là đủ: nhanh, miễn phí, không gửi gì ra internet. Bạn chỉ cần hộp thư thật khi muốn chắc thư xác minh thực sự tới được Gmail hay iCloud, khi chạy test trên môi trường thật, hoặc khi luồng đi qua một dịch vụ bên thứ ba có kiểm tra tên miền email. Lúc đó, một hộp thư thật đọc được qua API là cách gọn nhất để Playwright, Selenium hay Cypress tự lấy mã.
Bốn loại email dùng để test
| Loại | Hợp với | Không hợp với |
|---|---|---|
| Máy chủ thư giả trong staging | Test tích hợp, test tự động app của bạn | Kiểm tra thư có tới Gmail, iCloud thật; dịch vụ bên thứ ba |
| Alias Gmail (dấu +) của hộp thư nhóm QA | Test tay, vài tài khoản, mỗi lần chạy một hậu tố | Dịch vụ chặn dấu + hoặc gộp biến thể; test cần nhiều hộp thư tách biệt |
| Mail tạm | Kiểm tra nhanh luồng không quan trọng | Dịch vụ chặn tên miền mail tạm; tự động hóa cần ổn định |
| Hộp thư Gmail, iCloud thật qua API | E2E trên môi trường thật, dịch vụ bên thứ ba, chạy trong CI | Luồng cần gửi thư đi, tài khoản phải giữ lâu, kiểm thử tải |
Alias rẻ và tiện cho test tay, nhưng mọi biến thể về cùng một hộp và nhiều dịch vụ gộp chúng làm một; bài tạo nhiều email từ 1 Gmail nói rõ giới hạn này.
Chọn theo tình huống test
- Unit test, integration test của app bạn: máy chủ thư giả. Không tốn tiền, không phụ thuộc mạng.
- Smoke test sau khi phát hành: mỗi lần chạy một hộp thư thật, để chắc thư xác minh thực sự rời hệ thống của bạn và tới được nhà cung cấp lớn.
- Kiểm tra cả @gmail.com lẫn @icloud.com: người dùng thật của bạn dùng cả hai, nên chạy mỗi định dạng ít nhất một lần.
- Tích hợp dịch vụ bên thứ ba: chỉ tạo đúng số tài khoản test cần dùng, và đọc điều khoản của dịch vụ đó về tài khoản tạo tự động.
- Kiểm thử tải: đừng dùng hộp thư thật hay tạo tài khoản thật hàng loạt; dùng staging và máy chủ thư giả.
Luồng E2E với hộp thư thật qua API
Với OTPGmail, luồng chuẩn có năm bước:
- Gọi
POST /v1/ordersvớiservicevàdomain(gmail.comhoặcicloud.com), kèm headerIdempotency-Keymới cho mỗi đơn. App của bạn không có trong danh sách thì dùng mãot("Dịch vụ bất kỳ", giá cao hơn); test trên một dịch vụ có sẵn thì lấy mã của nó từGET /v1/services. Phản hồi là một mảng đơn, mỗi đơn cóorderIdvàemail. - Điền
emailvào form đăng ký. - Gọi
GET /v1/orders/{orderId}mỗi 3–5 giây; mã hoặc link mới nhất nằm ở cuối mảngotp. - Nhập mã, kiểm tra kết quả.
- Hết thời gian chờ của bài test mà chưa có mã thì gọi
POST /v1/orders/{orderId}/cancelđể được hoàn tiền ngay (chỉ hủy được trước khi có mã đầu). Không gọi thì hệ thống tự hủy và hoàn 100% khi hết thời gian chờ, tối đa 25 phút.
Mã mẫu cho Node 18 trở lên, dùng được trong Playwright; địa chỉ gốc của API và cách lấy API key ghi trong tài liệu API:
import { randomUUID } from 'node:crypto';
const BASE = process.env.OTPGMAIL_BASE_URL;
const AUTH = { Authorization: `Bearer ${process.env.OTPGMAIL_API_KEY}` };
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
export async function rentInbox(service = 'ot', domain = 'gmail.com') {
const res = await fetch(`${BASE}/v1/orders`, {
method: 'POST',
headers: { ...AUTH, 'Content-Type': 'application/json', 'Idempotency-Key': randomUUID() },
body: JSON.stringify({ service, domain }),
signal: AbortSignal.timeout(15000),
});
const body = await res.json();
if (!body.success) throw new Error(body.error.code);
return body.data[0];
}
export async function waitForCode(orderId, maxMs = 300000) {
const end = Date.now() + maxMs;
while (Date.now() < end) {
const res = await fetch(`${BASE}/v1/orders/${orderId}`, { headers: AUTH });
const order = (await res.json()).data;
if (order.otp.length) return order.otp.at(-1).code;
if (order.status === 'cancelled') return null;
await sleep(4000);
}
await fetch(`${BASE}/v1/orders/${orderId}/cancel`, { method: 'POST', headers: AUTH });
return null;
}
Trong bài test, gọi rentInbox(), điền email vào form, rồi waitForCode(orderId) trước khi nhập mã. Nếu bạn tự viết phần thử lại cho lệnh tạo đơn bị timeout, hãy gửi lại đúng Idempotency-Key của lần đầu để không tạo đơn trùng.
Giới hạn và chi phí cần tính trước khi đưa vào CI
- Chi phí: mỗi lần chạy là một lượt thuê, vài trăm đến vài nghìn đồng tùy dịch vụ. Chỉ trừ tiền khi đã cấp được hộp thư; hết hàng (
NO_MAILS_AVAILABLE,OUT_OF_STOCK) thì không bị trừ. - Tốc độ gọi: mặc định tối đa 20 lệnh mỗi giây và 900 lệnh mỗi phút cho một API key; vượt thì nhận
RATE_LIMITED(HTTP 429) kèm headerRetry-After. Mỗi lệnh thuê tối đa 200 hộp thư. - Số mã: nhận thêm mã không tốn thêm tiền trong thời hạn đơn nếu dịch vụ gửi nhiều mã. Với "Dịch vụ bất kỳ", bản Gmail nhận một mã mỗi hộp thư, bản iCloud cho đọc mọi thư mới gửi tới hộp thư trong 24 giờ.
- Hộp thư chỉ nhận: không test được luồng cần trả lời thư.
- Thuê theo lô (1–200 hộp thư, cùng đơn giá thuê lẻ, giữ 24 giờ, chỉ mua trên web): lô Gmail có link lấy mã riêng cho từng hộp để dùng thẳng trong script mà không cần API key, nên phải giữ bí mật link. Lô không hoàn tiền cho hộp thư không nhận được mã.
- API key: để trong biến môi trường hoặc kho bí mật của CI, không đưa vào mã nguồn.
Thực hành tốt khi test bằng email thật
- Đặt thời gian chờ theo tốc độ thường gặp của dịch vụ; quá hạn thì hủy ngay để được hoàn tiền.
- Ghi log mã lỗi chứ không chỉ mã HTTP: hết hàng thì thử định dạng còn lại,
INSUFFICIENT_BALANCEthì cảnh báo nạp tiền,DOMAIN_UNAVAILABLEnghĩa là dịch vụ chưa có iCloud hoặc iCloud đang tạm tắt. - Gắn nhãn và dọn tài khoản test trên dịch vụ bên thứ ba; không dùng hộp thư thuê cho tài khoản cần giữ lâu.
- Tài khoản test cho developer và QA là mục đích hợp lệ; tạo tài khoản hàng loạt gây hại cho dịch vụ khác thì không.
Cách thuê trên web và theo lô cho developer có ở bài thuê Gmail cho developer test đăng ký; ví dụ Python và cách xử lý từng mã lỗi ở bài API nhận OTP qua email cho tool tự động.
Câu hỏi thường gặp
Có cần email thật để chạy test Playwright hay Selenium không?
Không phải lúc nào cũng cần. Test app của bạn trong staging thì máy chủ thư giả là đủ. Cần email thật khi kiểm tra thư tới được Gmail, iCloud, khi chạy trên môi trường thật hoặc khi luồng đi qua dịch vụ bên thứ ba có kiểm tra tên miền.
Dùng alias Gmail dấu + cho test tự động có ổn không?
Ổn cho test tay và số lượng nhỏ, nếu dịch vụ chấp nhận dấu +. Không ổn khi dịch vụ gộp các biến thể làm một tài khoản, hoặc khi bạn cần nhiều hộp thư thật sự tách biệt.
Mã không về trong CI thì có mất tiền không?
Với thuê lẻ thì không: hủy trước khi có mã, hoặc để hệ thống tự hủy khi hết thời gian chờ, bạn được hoàn 100% vào số dư. Riêng thuê theo lô không hoàn tiền cho hộp thư không nhận được mã.
Nên test bằng @gmail.com hay @icloud.com?
Nên chạy cả hai, vì người dùng thật của bạn dùng cả hai. Nếu chỉ chọn một để tiết kiệm, iCloud thường rẻ hơn Gmail, tùy dịch vụ; dịch vụ chưa có iCloud sẽ trả lỗi DOMAIN_UNAVAILABLE, khi đó chuyển sang gmail.com.
