Почему почтовый провайдер — неподходящая форма для QA
Испробованы все способы снабдить набор тестов email-адресами, и каждый течёт в своём месте:
| Подход | Во что обходится | Где ломается |
|---|---|---|
Один общий ящик (qa@company.com) | Настраивать нечего. | Каждый тест читает почту любого другого теста. Первый же опрос нового прогона находит код из прошлого прогона. Параллельные прогоны невозможны. |
Плюс-адресация (qa+run42@company.com) | Настраивать нечего, если провайдер это поддерживает. | Это всё равно один ящик: одна квота, один логин IMAP на всю команду, и форма регистрации, которая обрезает метку или вовсе отказывает +. |
| Ящик на каждого тестировщика от провайдера | Место на каждого и заявка на его создание. | Никто не разворачивает ящик на каждый прогон теста. Тестировщики переиспользуют свой, и проблема общего ящика возвращается — просто по одному человеку за раз. |
| Catch-all-домен, направленный сюда | Одна DNS-запись, один раз. | Ящик публичен для любого, кто знает адрес, а почта живёт 5 дней. Оба свойства нормальны для кода, который важен одиннадцать секунд; ни одно из них не годится для настоящей почты клиентов. |
Четвёртая строка — это и есть тема этого руководства. Тот же самый механизм, на котором работают публичные одноразовые домены, — сервер, принимающий любой адрес на домене вместо списка ящиков, — применённый к имени, которым пользуетесь только вы.
Единственная DNS-запись
У вашего DNS-провайдера добавьте одну-единственную MX-запись на apex домена, которым вы владеете. Больше никаких записей, никакого TXT для доказательства чего-либо, никакого аккаунта на этом сайте:
MX-запись для домена10 smtp.grabmail.io
- Опубликуйте MX. Приоритет 10, указывает на
smtp.grabmail.io. Удалите любые другие MX на домене — почта может доставляться только в одно место, а оставшаяся запись отправит часть писем куда-то ещё. - Дождитесь DNS. Обычно минуты; иногда — TTL той записи, что стояла раньше.
dig MX qa-example.comпокажет, когда изменение вступило в силу. - Отправьте одно письмо на любой адрес на этом домене. Именно первая доставка подключает домен: в этот момент сервер ищет MX, видит там себя и принимает письмо. С этого момента любой адрес на домене становится ящиком.
Схема именования, которая говорит, кто создал ящик
Раз любой адрес действителен, часть до @ свободна для того, чтобы нести информацию, — а в два часа ночи, с упавшим прогоном и открытым ящиком, вам захочется, чтобы она её несла. Три части, соединённые дефисами, в таком порядке:
| Часть | Пример | Что это даёт |
|---|---|---|
| Кто создал | signup, reset, e2e, alice | Одного взгляда достаточно, чтобы понять, какому сценарию или какому человеку принадлежит ящик. |
| Какой прогон | 1234567890 (id прогона CI), имя ветки, дата | У всех ящиков одного прогона пайплайна общий токен, по которому можно искать. |
| Случайность | 3f9a1c2e | Восемь символов. Именно эта часть не даёт двум тестам, двум шардам или двум повторам делить один ящик. |
| Никогда: ничего настоящего | имя клиента, настоящий id пользователя, номер тикета, называющий клиента | Ящик публичен для любого, кто знает адрес. В адресе не должно быть ничего, что стоило бы знать постороннему. |
// one helper, every runner: who made it, which run, and eight random characters
export const testAddress = (who: string, run = process.env.GITHUB_RUN_ID ?? 'local') =>
`${who}-${run}-${crypto.randomUUID().slice(0, 8)}@qa-example.com`;
testAddress('signup'); // signup-1234567890-3f9a1c2e@qa-example.comЧтение любого адреса — те же три вызова
Для собственного домена в API ничего не меняется. Тот же вызов списка писем, тот же вызов письма, то же удаление — без ключа на публичном уровне, и единственное, что отличается, — это домен в адресе:
$ curl -sG https://grabmail.io/api/v1/mailbox --data-urlencode "address=signup-1234567890-3f9a1c2e@qa-example.com"А значит, любой помощник на этом сайте после изменения одной константы работает без переделок: фикстура Playwright, задача Cypress, модули Python и Node. Стоит знать три вещи, о которых на публичных доменах даже не приходится думать:
404значит, что MX ещё не появился- Список писем отвечает
404для домена, который здесь не обслуживается. На собственном домене это проблема DNS — запись ещё не распространилась или указывает не туда, — а не проблема API. Сначала проверьтеdig MX. - Алиас работает и на вашем домене
- У каждого ящика, на любом домене, есть второй адрес на отдельном домене, который доставляет в него почту, но не может её прочитать. Отдавайте его сайту, когда не хотите, чтобы тот мог открыть ящик; опрашивайте при этом собственный адрес.
- Загруженный адрес читается постранично
- Catch-all-адрес, собирающий возвраты или уведомления за целый день, может содержать больше одной страницы из 200. Передавайте
nextобратно какbefore, пока он не станетnull.
Как сохранить тестовый домен пригодным к использованию
Собственный домен не состоит ни в одном чёрном списке одноразовой почты, и зачастую именно поэтому его и берут: тестируемое приложение отказывает grabmail.io и любому другому публичному домену — именно так, как и должно. Четыре привычки сохраняют это положение вещей:
- Используйте выделенный домен —
qa-example.com, а не поддомен продакшена и не домен, на который пишут клиенты. Сервис принимает только регистрируемые домены (example.com, но никогдаmail.example.com), и у тестового домена не должно быть никакой другой работы. - Не публикуйте его. Списки строятся из того, что появляется на публичных сайтах временной почты и в общих подборках адресов. У домена, который встречается только в вашем собственном наборе тестов, нет пути в такие списки.
- Закройте его SPF и DMARC. Домен, который только принимает почту и не имеет SPF, — это домен, письмо от имени которого может подделать кто угодно; две записи закрывают эту дыру и ничего не стоят.
- Не направляйте на него настоящую почту. В тот момент, когда staging-система отправляет уведомления клиентам на catch-all-адрес, публичный ящик начинает хранить данные клиентов. Тестовые домены — для тестовой почты.
Если приложение отказывает любому catch-all-домену — некоторые проверки на мошенничество так и делают, проверяя, принимается ли случайный адрес на домене, — есть платный пул ничем не примечательных доменов .com, которые держат подальше от списков, описанный на странице тарифов. В руководстве «Почему формы регистрации блокируют одноразовую почту» объясняется, что видит каждый тип проверки.
Для команды: один домен, много тестировщиков, много пайплайнов
Один catch-all-домен обслуживает всех, потому что различаться должна только часть до @, а она ничего не стоит. Команде нужна лишь договорённость о схеме именования выше и три небольших соглашения:
- Префикс на каждый пайплайн и каждого человека
e2e-,nightly-,alice-. Поиск по префиксу в логе задания находит все ящики, которые оно создало; поиск по префиксу коллеги находит баг, которым он занимается.- Ничего общего, никогда
- На домене нет «командного ящика» и нет фикстуры, выдающей фиксированный адрес. Если двум людям нужен один и тот же ящик, один просто присылает адрес другому.
- Лимиты запросов действуют на клиента
- Одно чтение в секунду на адрес, 1200 запросов в минуту на клиента — раннер CI — это один клиент, ноутбук — другой. Команда из десяти человек, одновременно запускающих наборы тестов, — это десять клиентов, а не один.
Действующие ограничения
Собственный домен получает точно такое же обслуживание, как и публичные, с теми же потолками. Ни один из них не настраивается, и ни один не представляет проблемы для набора тестов:
| Ограничение | Значение | Что это значит для QA |
|---|---|---|
| Срок хранения | 5 дней на письмо | Каждый прогон создаёт собственную почту; ничего никогда не читается из прошлой недели. Точные правила. |
| Вложения | 5 MB на письмо | Хватает на PDF со счётом или экспорт в CSV; письмо большего размера отклоняется ещё на этапе SMTP, так что отправитель об этом узнаёт. |
| Чтения | 1 в секунду на адрес, 1200 в минуту на клиента | Двадцать ящиков, опрашиваемых раз в секунду, с одного раннера. Сверх этого — 429 с Retry-After. |
| Приватность | Отсутствует — прочитать может любой, кто знает адрес | Случайная часть адреса, неопубликованный домен и никакой настоящей почты клиентов. |
| Отправка | Отсутствует | Домен только принимает. Ваше приложение отправляет через собственного провайдера, как и в продакшене. |
Прежде чем считать задачу закрытой
- Выделенный регистрируемый домен с одной MX-записью:
10 smtp.grabmail.io, и никаких других MX. - Одно письмо отправлено на любой адрес на нём и считано обратно через API.
- Опубликованы SPF и DMARC, чтобы никто не мог отправлять письма от имени домена.
- Помощник для именования — кто, какой прогон, восемь случайных символов — используется в каждом наборе тестов.
- Домен хранится в конфигурации окружения, а не в коде, скриншотах или тикетах.
- На него не направлено ничто, что отправляет настоящую почту клиентам.
Дальше — уже написанные наборы тестов: Playwright с доменом в фикстуре, дисциплина, не привязанная к конкретному раннеру, и workflow для GitHub Actions с доменом в переменной.
Вопросы
Можно ли использовать поддомен, вроде test.company.com?
Нет — сервис принимает только регистрируемые домены (company.com или company.co.uk), потому что тот, кто контролирует домен, контролирует и любое имя под ним, а две стороны не должны иметь возможность делить пересекающиеся половины одного пространства имён. Зарегистрируйте недорогой выделенный домен для тестирования — это в любом случае более правильная практика.
Через сколько заработает MX-запись?
Как только DNS начнёт её отдавать, а это обычно минуты. Если раньше был другой MX, действует его TTL. Домен подключается первым же письмом, которое приходит после этого, — больше ничего происходить не должно, а dig MX покажет, когда запись заработала.
Это что-нибудь стоит?
Нет. Подключение домена, любой адрес на нём и API бесплатны, без аккаунта. Единственная платная вещь на этом сайте — пул доменов, которые держат подальше от чёрных списков одноразовой почты, для тех, кто не может использовать собственный домен.
Может ли кто-то ещё читать почту на моём домене?
Да, любой, кто знает адрес, — точно так же, как и на публичных доменах. Собственный домен меняет только угадываемость: его адреса стоят на имени, которым больше никто не пользуется. Случайные части адреса и неопубликованный домен делают угадывание практически невозможным; но приватным ящик от этого не становится — здесь этого не делает ничто.
Что происходит с почтой, отправленной на домен до того, как я его подключил?
Сюда ничего не доходит, пока MX не указывает сюда. Почта, отправленная, пока действовала старая запись, уходила на старый сервер либо возвращалась отправителю; почта, отправленная после переключения DNS, приходит и подключает домен.
Как отключить домен?
Удалите MX-запись. Новая почта перестаёт приходить сразу же; то, что уже лежит в ящиках, само истечёт в течение 5 дней. После этого о домене ничего не сохраняется.
Приложение отказывает и моему тестовому домену тоже. Что теперь?
Некоторые проверки на мошенничество отказывают любому домену, принимающему случайный адрес, — это catch-all-проверка, а не сверка со списком. Для таких случаев нужен домен, который ведёт себя как обычный почтовый провайдер, а именно это и даёт платный пул; в руководстве «Почему формы регистрации блокируют одноразовую почту» объясняется, с какой именно проверкой вы столкнулись.


