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

Подписаться

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

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

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

.
Щелкаем выключателем: автоматизация тестирования фича-флагов
29.07.2025 00:00

Автор: Green Report
Оригинал статьи
Перевод: Ольга Алифанова

Флаги-функции (feature flags) – это мощный инструмент контролируемого выпуска новых возможностей, проведения A/B-тестирования и экспериментов. Однако для инженеров по автоматизации тестирования такие фичи, скрытые за флагами, представляют собой отдельную проблему. Как обеспечить полное тестовое покрытие функциональности, которая может быть отключена в одной среде и включена в другой? В этой статье мы рассмотрим стратегии автоматизации тестирования фичей, скрытых за фича-флагами, включая настройку тестов с учётом флагов, проверку как включённого, так и отключённого состояния, и программное управление флагами для упрощения тестирования.

Что такое фича-флаги и почему они важны?

Фича-флаги (также известные как переключатели фич) — это мощный инструмент в разработке программного обеспечения, позволяющий управлять активацией конкретных функций без необходимости деплоя нового кода. Благодаря возможности динамического включения и отключения, команды могут постепенно внедрять новую функциональность, проводить A/B-тесты и управлять экспериментальными функциями в разных окружениях.

По сути фича-флаги — это условные конструкции в коде. Например, флаг может определять, увидит ли пользователь новый дизайн главной страницы или останется на старом. Управление флагами обычно осуществляется через конфигурационные файлы, API или платформы управления фичами, такие как LaunchDarkly или Flagsmith. Это делает их гибкими и удобными в использовании.


Сравнение работы приложения с выключенным фича-флагом (слева) и включённым (справа)

Почему фича-флаги важны?

  • Контролируемый выпуск: Фича-флаги позволяют выпускать новые возможности для конкретных групп пользователей или в определённых средах, снижая риск массовых сбоев.
  • A/B-тестирование: С их помощью можно протестировать несколько вариантов реализации функции и измерить реакцию пользователей до принятия окончательного решения.
  • Быстрые эксперименты: Разработчики могут внедрять новые идеи, не рискуя стабильностью продакшн-среды.
  • Безопасный откат: Если новая функция вызывает проблемы, её можно мгновенно отключить без необходимости выкладывать новую версию приложения.

Несмотря на все плюсы фича-флагов для разработки, они добавляют сложности для команд тестирования. Тестировщики должны удостовериться, что функциональность работает корректно как при включённом флаге, так и при его отключении. Кроме того, флаги могут повлиять на надёжность автотестов, если их состояние отличается между средами. Инженерам по автоматизации приходится особенно трудно — им нужно писать тесты, которые будут динамически учитывать состояние флага, при этом обеспечивая полное покрытие.

Настройка тестов с учётом фича-флагов: когда тестировать

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

Что такое тесты с учётом флагов?

Тесты, чувствительные к флагам, проверяют состояние фича-флага перед выполнением теста. Если функция включена — тест проверяет новую функциональность. Если отключена — тест либо пропускается, либо проверяет, что функция действительно отсутствует.

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

Реализация тестов, чувствительных к флагам

Пример настройки таких тестов в JavaScript-фреймворке Playwright:

Шаг 1: Получение статуса фича-флага

Можно использовать API-запрос, конфигурацию окружения или мок-данные, чтобы определить активность флага:

async function getFeatureFlag(flagName) {
const response = await fetch(`https://feature-flags/status?flag=${flagName}`);
const data = await response.json();
return data.enabled; // Возвращает true или false
}

Шаг 2: Условное выполнение теста

Как только статус флага получен, принимается решение: запускать тест или нет.

const { test } = require('@playwright/test');
 
test('Тест новой функции', async ({ page }) => {
const featureEnabled = await getFeatureFlag('new_feature');
if (featureEnabled) {
await page.goto('https://example.com/new-feature');
await page.getByTestId('new-feature-button').click();
const message = await page.locator('#feature-result').textContent();
expect(message).toBe('Feature works!');
} else {
test.skip('Функция отключена, тест пропущен.');
}
});

Преимущества тестов с учётом фича-флагов

  • Снижение количества ложных срабатываний: Предотвращают ложноотрицательные результаты, возникающие при запуске тестов для отключённых функций.
  • Эффективное выполнение тестов: Позволяют сосредоточить ресурсы на актуальных тест-кейсах, пропуская ненужные.
  • Прозрачная отчётность: Чётко указывают, был ли тест пропущен из-за состояния флага, что улучшает отслеживаемость.

Когда необходимы чувствительные к флагам тесты?

Тесты с учётом состояния фича-флагов особенно полезны в следующих ситуациях:

  • Динамичные тестовые окружения: Когда фича-флаги часто переключаются между различными средами (например, стейдж, прод).
  • Частичные релизы: Когда функция включена только для определённых групп пользователей или регионов.
  • Бета-тестирование: При тестировании экспериментальных функций, которые ещё не выкатились в полном объёме.

Две стороны фича-флага: тестирование обеих ситуаций

Фича-флаг по своей сути создаёт два разных состояния для функциональности: включённое и выключенное. Полноценное тестирование требует проверки обоих сценариев, чтобы убедиться, что приложение работает корректно вне зависимости от состояния флага. Это критически важно для выявления регрессий, проверки целостности функционала и корректного скрытия неактивных функций.

Зачем тестировать оба состояния?

  • Флаг включён: необходимо протестировать работу новой функциональности, взаимодействие с другими компонентами и интеграцию.
  • Флаг выключен: нужно убедиться, что приложение остаётся стабильным, новая функция скрыта или недоступна, и нет побочных эффектов.

Чтобы эффективно протестировать оба состояния, можно:

  • Использовать параметризованные тесты для переключения между состояниями внутри одного набора тестов.
  • Поддерживать отдельные наборы тестов для включённого и выключенного флага.

Вариант 1: Параметризованные тесты

Параметризованные тесты позволяют динамически переключать состояние фиче-флага во время выполнения тестов, уменьшая дублирование кода.

const { test, expect } = require("@playwright/test");
const scenarios = [
{ state: "enabled", flagValue: true },
{ state: "disabled", flagValue: false },
];
scenarios.forEach(({ state, flagValue }) => {
test(`Функция ${state}: Проверка поведения`, async ({ page }) => {
// Программно задать состояние фиче-флага
await setFeatureFlag("new_feature", flagValue);
await page.goto("https://example.com");
if (flagValue) {
// Проверка функциональности при включённом флаге
await page.getByTestId("new-feature-button").click();
const result = await page.locator("#feature-result").textContent();
expect(result).toBe("Feature works!");
} else {

            // Проверка отсутствия элемента и стабильности приложения при выключенном флаге

            await expect(page.getByTestId("new-feature-button")).not.toBeVisible();

        }

    });

});

Вариант 2: Отдельные наборы тестов

Отдельные наборы тестов могут быть полезны в ситуациях, когда состояния фиче-флагов фиксированы в разных окружениях (например, staging и production), или когда предпочтительнее запускать тесты по отдельности.

test.describe("Фича включена", () => {
test.beforeEach(async () => {
await setFeatureFlag("new_feature", true);
});
test("Проверка работы новой функциональности", async ({ page }) => {
await page.goto("https://example.com");
await page.getByTestId("new-feature-button").click();
const result = await page.locator("#feature-result").textContent();
expect(result).toBe("Feature works!");
});
});
test.describe("Фича отключена", () => {
test.beforeEach(async () => {
await setFeatureFlag("new_feature", false);
});
test("Проверка отсутствия функциональности", async ({ page }) => {
await page.goto("https://example.com");
await expect(page.getByTestId("new-feature-button")).not.toBeVisible();
});
});

Рекомендации по тестированию обеих ситуаций:

  • Изолируйте влияние фича-флага: Убедитесь, что тесты проверяют только влияние конкретного флага, не затрагивая другие части функциональности.
  • Используйте автоматизацию для стабильности: Программно переключайте состояние флагов в тестовой среде, чтобы обеспечить предсказуемость условий.
  • Щедро применяйте утверждения (assertions): Проверяйте как наличие, так и отсутствие элементов, функциональности и побочных эффектов в каждом сценарии.

Автоматизация обновлений фича-флагов: контроль тестов без лишних усилий

Ручное переключение фича-флагов перед запуском тестов может быть трудозатратным и подверженным ошибкам — особенно при работе с несколькими окружениями или частыми тестовыми запусками. Автоматизация управления фиче-флагами обеспечивает стабильные условия тестирования и позволяет автоматизированным тестам гибко адаптироваться при необходимости.

Зачем автоматизировать обновление фича-флагов?

  • Стабильность: Гарантирует нужное состояние флага перед каждым запуском теста.
  • Эффективность: Экономит время, исключая ручную подготовку.
  • Масштабируемость: Обеспечивает поддержку больших тестовых пакетов и различных окружений с минимальными усилиями.
  • Гибкость: Позволяет изменять состояние флага на лету прямо во время выполнения теста.

1. Использование API для управления фиче-флагами

Многие системы управления фича-флагами предоставляют API для программного включения/отключения флагов. Это самый распространённый и эффективный способ.

async function setFeatureFlag(flagName, isEnabled) {
const response = await fetch('https://feature-flags/update', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ flag: flagName, enabled: isEnabled })
});
if (!response.ok) {
throw new Error(`Не удалось обновить фиче-флаг: ${flagName}`);
}
}

2. Локальная имитация фича-флагов

В некоторых случаях проще имитировать состояние фича-флага локально прямо в тестовой среде.

test('Тест с мокированным фиче-флагом', async ({ page }) => {
await page.addInitScript(() => {
window.featureFlags = { new_feature: true }; // Мокаем фиче-флаг
});
await page.goto('https://example.com');
await expect(page.getByTestId("new-feature-button")).toBeVisible();
});

Этот подход особенно удобен, если логика фича-флагов реализована на стороне клиента и её можно переопределить в ходе теста.

3. Использование конфигурационных файлов

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

const fs = require('fs');
function updateConfig(flagName, isEnabled) {
const configPath = './config/feature-flags.json';
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
config[flagName] = isEnabled;
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
}
// Пример использования в настройке тестов
test.beforeAll(() => {
updateConfig('new_feature', true); // Включаем фичу
});

4. Обновление фича-флагов через Local Storage

Некоторые приложения управляют фича-флагами в локальном хранилище браузера (localStorage). В таких случаях автоматизационные скрипты могут напрямую изменять localStorage, чтобы обновить конкретный флаг, при этом сохраняя состояние остальных. Этот метод быстрый и минимально влияет на состояние приложения.

test('Обновление конкретного фича-флага в localStorage', async ({ page }) => {
// Шаг 1: Переходим на сайт приложения
await page.goto('https://example.com');
// Шаг 2: Получаем и обновляем состояние фича-флага
await page.evaluate(() => {
// Получаем все фиче-флаги из localStorage
const flags = JSON.parse(localStorage.getItem('featureFlags') || '{}');
// Обновляем конкретный флаг, не затрагивая остальные
flags['new_feature'] = true; // Включаем флаг 'new_feature'
// Сохраняем обновленные флаги обратно в localStorage
localStorage.setItem('featureFlags', JSON.stringify(flags));
});
// Шаг 3: Перезагружаем страницу, чтобы применить изменения
await page.reload();
// Шаг 4: Проверяем, что флаг сработал
await expect(page.getByTestId("new-feature-button")).toBeVisible();
});

Лучшие практики автоматизации фича-флагов:

  • Централизуйте управление флагами: создайте переиспользуемые утилиты для работы с флагами в тестах.
  • Обрабатывайте ошибки грамотно: добавляйте обработку ошибок в скрипты, чтобы тесты не запускались с некорректным состоянием флагов.
  • Документируйте зависимости: чётко указывайте, какие флаги нужны для каждого теста, чтобы избежать путаницы.
  • Очистка после тестов: возвращайте флаги к исходным состояниям, чтобы обеспечить стабильность следующих запусков.

Заключение

Фича-флаги — мощный инструмент для управления релизами, экспериментами и поведением приложения. Включение их поддержки в автоматизацию тестирования обеспечивает полное покрытие, снижает риски и упрощает процесс проверки.

Настраивая чувствительные к флагам тесты, покрывая оба состояния (включено и выключено) и автоматизируя управление флагами через API, имитаторы, конфигурационные файлы или localStorage, мы создаём надёжную и эффективную стратегию тестирования.

С этими практиками фича-флаги становятся активом, а не препятствием на пути к качественному ПО.

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