Что пишут в блогах

Подписаться

Что пишут в блогах (EN)

Разделы портала

Онлайн-тренинги

.
Тестирование 2FA с Playwright и Mailosaur
24.08.2026 00:00

Автор: Филип Рик (Filip Hric)
Оригинал статьи
Перевод: Ольга Алифанова

Когда вы пишете end-to-end тесты, аутентификация часто становится первым барьером. Невозможно протестировать реальную функциональность приложения, не пройдя сначала экран логина. Но современные методы аутентификации могут усложнять автоматизацию, используя несколько факторов, которые трудно автоматизировать (в этом и заключается смысл 2FA).

Обычно с этим справляются, либо отключая такие методы в тестовых окружениях, либо используя различные обходные решения. Кто-то может сказать, что это уже не настоящее e2e-тестирование. Честно говоря, это, скорее, тема для отдельной дискуссии, но критика подхода с обходом логина определённо имеет основания.

Так как же правильно работать с аутентификацией?

Уже много лет моим основным решением является Mailosaur. Это сервис для тестирования, который предоставляет виртуальные email-адреса и номера телефонов для автоматизации. Представьте его себе как тестовый почтовый ящик, к которому тесты получают доступ программно. Можно:

  • Отправлять письма на уникальные адреса и получать их через API
  • Получать SMS-сообщения на виртуальные номера
  • Генерировать коды для приложений-аутентификаторов без физического устройства

Давайте посмотрим, как это настроить.

Начало работы

Сначала потребуется аккаунт Mailosaur. Он нужен для создания виртуальных email-адресов и номеров телефонов для тестов. После этого установите клиент Mailosaur:

npm install --save-dev mailosaur

В аккаунте Mailosaur вам понадобятся две вещи:

  • API Key: находится в настройках аккаунта
  • Server ID: уникальный идентификатор тестового почтового ящика

Их лучше хранить в секрете (особенно API key), поэтому рекомендуется сохранять их в переменных окружения, например в .env файле или в CI/CD пайплайне.

# .env
MAILOSAUR_API_KEY=your_api_key_here
MAILOSAUR_SERVER_ID=your_server_id_hereТеперь рассмотрим каждый метод аутентификации.

Метод 1: magic links по email

Magic links становятся всё более популярными. Их можно использовать вместо пароля, но по сути это то же самое, что и при сбросе пароля. Сервер генерирует ссылку, которая даёт доступ к определённому действию (вход или смена пароля), и отправляет её на email владельца аккаунта. Поток выглядит так:


Как видно из диаграммы выше, основная сложность в том, что в определённый момент поток отрывается от браузера. Для браузерной автоматизации это проблема. Как попасть в почтовый ящик?

Здесь помогает Mailosaur. Он позволяет программно получать доступ к почтовому ящику, созданному для теста. Из него извлекается нужная информация (письмо со ссылкой), после чего тест продолжается.

Вот как может выглядеть такой тест в Playwright:

import { test, expect } from '@playwright/test';
import { default as MailosaurClient } from 'mailosaur';
test('should send a magic link to the email address', async ({ page }) => {
const mailosaur = new MailosaurClient(process.env.MAILOSAUR_API_KEY as string);
const serverId = process.env.MAILOSAUR_SERVER_ID as string;
const testEmail = `testing-email@${serverId}.mailosaur.io`;
await page.goto('/auth/magic-link');
await page.getByRole('textbox', { name: 'email' }).fill(testEmail);
await page.getByText('Send Magic Link').click();
const message = await mailosaur.messages.get(serverId, {
sentTo: testEmail
});
const link = message.html?.links?.[0]?.href;
await page.goto(link as string);
await expect(page.getByText('Authentication Successful!')).toBeVisible();
});

Примечание: чтобы использовать переменные из .env в Playwright, нужно импортировать пакет dotenv в файл playwright.config.ts.

import 'dotenv/config';
dotenv.config();
/// rest of the config file...

Ключевая часть здесь — метод mailosaur.messages.get(). Он автоматически ожидает поступления письма и парсит его содержимое. Ссылки доступны через message.html.links. Каждый объект ссылки содержит:

{
href: 'https://example.com/verify?token=abc123',
text: 'Verify your account'
}

Если в письме несколько ссылок, их можно отфильтровать:

const verifyLink = message.html.links.find(
link => link.href.includes('/verify')
);

Метод 2: SMS-коды подтверждения

По сути, аутентификация через SMS похожа на magic links, только вместо письма отправляется числовой код на телефон. Пользователь вводит этот код, чтобы подтвердить владение номером. Поток выглядит так:


С помощью Mailosaur можно создать виртуальный номер телефона, который будет получать SMS.

После этого его можно использовать в тестах. Вот пример автоматизации такого потока:

import { test, expect } from '@playwright/test';
import { default as MailosaurClient } from 'mailosaur';
test('should send SMS code and verify authentication', async ({ page }) => {
const mailosaur = new MailosaurClient(process.env.MAILOSAUR_API_KEY as string);
const serverId = process.env.MAILOSAUR_SERVER_ID as string;
const testPhone = '+12345678910'; // your Mailosaur phone number
await page.goto('/auth/sms');
await page.getByRole('textbox', { name: 'phone' }).fill(testPhone);
await page.getByText('Send SMS Code').click();
await expect(page.getByText('SMS sent! Enter the verification code.')).toBeVisible();
const message = await mailosaur.messages.get(serverId, {
sentTo: testPhone
});
// extract 6 digit code from SMS
const otpMatch = message.text?.body?.match(/\b(\d{6})\b/);
expect(otpMatch).toBeTruthy();
const otpCode = otpMatch![1];
await page.getByRole('textbox', { name: 'otp' }).fill(otpCode);
await page.getByText('Verify Code').click();
await expect(page.getByText('Authentication Successful!')).toBeVisible();
});

Как и в случае с email, Mailosaur автоматически извлекает коды подтверждения из SMS. Они доступны через message.text.codes. Если кодов несколько (что бывает редко), все они будут в массиве codes.

const code = message.text.codes[0].value; // e.g., "564214"

Метод 3: приложения-аутентификаторы (TOTP)

Приложения вроде Google Authenticator или Authy генерируют одноразовые коды на основе времени (TOTP). Эти коды меняются каждые 30 секунд и создаются на основе общего секрета. В этом процессе есть два этапа.

На первом этапе приложение-аутентификатор настраивается с использованием общего секрета.


На втором этапе используется код, сгенерированный приложением, который вводится в форму входа. Затем он проверяется на сервере.


Секрет обычно представлен в виде QR-кода или строки символов при настройке. При интеграции с Mailosaur первый этап можно настроить вручную в сервисе Mailosaur.

 

Это позволяет взаимодействовать с аутентификатором при ручном тестировании. Но можно настроить всё и автоматически в тестах. Вот пример теста для TOTP-аутентификации:

import { test, expect } from '@playwright/test';
import { default as MailosaurClient } from 'mailosaur';
test('login with authenticator app', async ({ page }) => {
const mailosaur = new MailosaurClient(process.env.MAILOSAUR_API_KEY as string);
const serverId = process.env.MAILOSAUR_SERVER_ID;
const testEmail = `testing-totp@${serverId}.mailosaur.net`;
await page.goto('/auth/totp');
await page.getByRole('textbox', { name: 'Email or Phone' }).fill(testEmail);
await page.getByText('Setup TOTP').click();
const secretElement = page.locator('code');
const sharedSecret = await secretElement.textContent() as string;
const otp = await mailosaur.devices.otp(sharedSecret);
await page.locator('#totpCode').fill(otp.code as string);
await page.getByRole('button', { name: 'Verify & Complete Setup' }).click();
await expect(page.getByText('Authentication Successful!')).toBeVisible();
});

Как видно, общий секрет извлекается из элемента на странице. Обычно такая возможность есть на страницах настройки TOTP. Если нет, можно декодировать QR-код, чтобы получить ключ.


Общие подводные камни и советы

Проблемы с таймингом TOTP-кодов

TOTP-коды истекают каждые 30 секунд. Если тест работает медленно или попадает на границу времени, код может устареть между генерацией и использованием. Чтобы это обработать:

Задержки доставки email и SMS

Хотя метод messages.get() автоматически ждёт сообщение, таймаут можно настроить:

const message = await mailosaur.messages.get(
serverId,
{ sentTo: testEmail },
{ timeout: 20000 } // 20 seconds instead of default 10 seconds
);

Очистка тестовых данных

Если почтовый ящик переполняется тестовыми данными, их можно очистить:

test.afterEach(async () => {
const mailosaur = new MailosaurClient(process.env.MAILOSAUR_API_KEY);
await mailosaur.messages.deleteAll(process.env.MAILOSAUR_SERVER_ID);
});

Использование критериев поиска для нахождения нужных писем

Вместо уникальных email-адресов можно искать письма по содержимому или теме. Это особенно полезно, когда несколько тестов отправляют письма на один адрес:

const message = await mailosaur.messages.get(serverId, {
sentTo: testEmail,
subject: 'Login',
body: 'verification code'
});

Для SMS обычно доступно ограниченное количество виртуальных номеров, поэтому используйте критерии поиска:

const message = await mailosaur.messages.get(serverId, {
sentTo: phoneNumber,
body: 'Your verification code' // Search by message content
});

Надеюсь, это поможет! Если материал оказался полезным, поделитесь им с теми, кто сталкивается с проблемами тестирования аутентификации. Также можно найти меня в Twitter или LinkedIn, где я делюсь дополнительными советами и приёмами тестирования.

Обсудить в форуме