Тестирование фича-флагов

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

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

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

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

Сравнение функциональности приложения с выключенным (слева) и включенным (справа) флагом

Важность

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

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

Настройка тестов с поддержкой переключения 

Простое выполнение автотестов без учета состояний флагов может привести к противоречивым результатам и потере времени. Поэтому необходимы тесты с поддержкой переключения (toggle-aware). Такие тесты динамически адаптируются в зависимости от того, включена или выключена фича. 

Что такое тесты с поддержкой переключения

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

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

Реализация тестов с поддержкой переключения

Пример настройки тестов с поддержкой переключения в фреймворке тестирования на 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; // Returns true or false
}

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

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

const { test } = require('@playwright/test');

test('New Feature 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('Feature is disabled, skipping the test.');
  }
});

Преимущества тестов с поддержкой переключения 

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

Когда использовать тесты с поддержкой переключения

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

Тестирование обоих сценариев 

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

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

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

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

  • Использовать параметризованные тесты для переключения между состояниями в одном тестовом наборе. 
  • Вести отдельные тестовые наборы для включенных и выключенных состояний.

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

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

const { test, expect } = require("@playwright/test");

const scenarios = [
  { state: "enabled", flagValue: true },
  { state: "disabled", flagValue: false },
];
                    
scenarios.forEach(({ state, flagValue }) => {
  test(`Feature ${state}: Validate behavior`, async ({ page }) => {
    // Mock or programmatically set the feature flag
    await setFeatureFlag("new_feature", flagValue);
                    
    await page.goto("https://example.com");
                    
    if (flagValue) {
      // Validate functionality when the feature is enabled
      await page.getByTestId("new-feature-button").click();
      const result = await page.locator("#feature-result").textContent();
      expect(result).toBe("Feature works!");
    } else {
      // Validate absence or stability when the feature is disabled
      await expect(page.getByTestId("new-feature-button")).not.toBeVisible();
    }
  });
});

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

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

test.describe("Feature Enabled", () => {
  test.beforeEach(async () => {
    await setFeatureFlag("new_feature", true);
  });
                      
  test("Validates new feature functionality", 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("Feature Disabled", () => {
  test.beforeEach(async () => {
    await setFeatureFlag("new_feature", false);
  });
                      
  test("Validates feature absence", async ({ page }) => {
    await page.goto("https://example.com");
    await expect(page.getByTestId("new-feature-button")).not.toBeVisible();
  });
});

Лучшие практики тестирования обоих сценариев:

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

Автоматизация обновления фича-флагов: плавное управление тестами 

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

Зачем автоматизируется обновление флагов

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

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(`Failed to update feature flag: ${flagName}`);
  }
}

2. Локальное мокирование флагов 

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

test('Test with mocked feature flag', async ({ page }) => {
  await page.addInitScript(() => {
    window.featureFlags = { new_feature: true }; // Mock feature flag
  });
                        
  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));
}
                    
// Example usage in a test setup
test.beforeAll(() => {
  updateConfig('new_feature', true); // Enable the feature
});

4. Обновление флагов через локальное хранилище

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

test('Update specific feature flag in local storage', async ({ page }) => {
  // Step 1: Navigate to the application
  await page.goto('https://example.com');
                    
  // Step 2: Retrieve and update the feature flag state
  await page.evaluate(() => {
    // Fetch all feature flags stored in local storage
    const flags = JSON.parse(localStorage.getItem('featureFlags') || '{}');
                    
    // Update the specific flag without altering others
    flags['new_feature'] = true; // Enable the 'new_feature' flag
                    
    // Save the updated flags back to local storage
    localStorage.setItem('featureFlags', JSON.stringify(flags));
  });
                    
  // Step 3: Reload the page to apply changes
  await page.reload();
                    
  // Step 4: Validate the updated flag's effect
  await expect(page.getByTestId("new-feature-button")).toBeVisible();
});

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

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

Выводы

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

The Green Report


Какой была ваша первая зарплата в QA и как вы искали первую работу?

Мега обсуждение в нашем телеграм-канале о поиске первой работы. Обмен опытом и мнения.

Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

Мы в Telegram

Наш официальный канал
Полезные материалы и тесты
Готовимся к собеседованию
Project- и Product-менеджмент

? Популярное

? Telegram-обсуждения

Наши подписчики обсуждают, как искали первую работу в QA. Некоторые ищут ее прямо сейчас.
Наши подписчики рассказывают о том, как не бояться задавать тупые вопросы и чувствовать себя уверенно в новой команде.
Обсуждаем, куда лучше податься - в менеджмент или по технической ветке?
Говорим о конфликтных ситуациях в команде и о том, как их избежать
$1100*
медианная зарплата в QA в июне 2023

*по результатам опроса QA-инженеров в нашем телеграм-канале

Собеседование

19%*
IT-специалистов переехало или приняло решение о переезде из России по состоянию на конец марта 2022

*по результатам опроса в нашем телеграм-канале

live

Обсуждают сейчас