Pairwise testing (также известное как all-pairs testing) - это комбинаторная техника тестирования, которая драматически сокращает размер тестового набора, сохраняя высокие показатели обнаружения дефектов. Тестируя все возможные комбинации пар параметров вместо всех возможных комбинаций всех параметров, pairwise testing достигает приблизительно 90% покрытия дефектов с долей тестовых случаев.
Проблема Комбинаторного Взрыва
Современные программные системы имеют многочисленные опции конфигурации и входные параметры. Тестирование всех комбинаций быстро становится невыполнимым.
Пример: Тестирование Конфигурации
Рассмотрим простой e-commerce checkout с 5 параметрами:
- Метод Оплаты: Кредитная Карта, PayPal, Банковский Перевод (3 опции)
- Доставка: Стандарт, Экспресс, Ночная (3 опции)
- Подарочная Упаковка: Да, Нет (2 опции)
- Купон: Применен, Нет (2 опции)
- Тип Пользователя: Гость, Зарегистрированный (2 опции)
Исчерпывающее тестирование: 3 × 3 × 2 × 2 × 2 = 72 тестовых случая
Pairwise testing: Приблизительно 12-15 тестовых случаев, покрывающих все pairwise взаимодействия
По мере увеличения параметров, экономия становится драматической:
Параметры | Опций Каждый | Исчерпывающий | Pairwise | Сокращение |
---|---|---|---|---|
5 | 3 | 243 | ~15 | 94% |
10 | 3 | 59,049 | ~20 | 99.97% |
20 | 2 | 1,048,576 | ~10 | 99.999% |
Алгоритм All-Pairs
Pairwise testing гарантирует, что для каждой пары параметров все комбинации их значений появляются в как минимум одном тестовом случае.
Математическое Обоснование
Исследование NIST показывает, что большинство дефектов программного обеспечения вызываются взаимодействиями максимум 2 параметров (приблизительно 70% дефектов). Добавление 3-way взаимодействий ловит ~90-95% дефектов.
Pairwise Покрытие: Для каждой пары (P1, P2) параметров и каждой комбинации (v1, v2), где v1 - значение P1 и v2 - значение P2, существует как минимум один тестовый случай, где P1 = v1 и P2 = v2.
Простой Пример
Параметры:
- Browser: Chrome, Firefox (2 значения)
- OS: Windows, Mac, Linux (3 значения)
- Network: WiFi, 4G (2 значения)
Исчерпывающий: 2 × 3 × 2 = 12 тестов
Pairwise набор:
Test | Browser | OS | Network |
---|---|---|---|
1 | Chrome | Windows | WiFi |
2 | Chrome | Mac | 4G |
3 | Chrome | Linux | WiFi |
4 | Firefox | Windows | 4G |
5 | Firefox | Mac | WiFi |
6 | Firefox | Linux | 4G |
6 тестов покрывают все pairwise комбинации вместо 12 исчерпывающих тестов.
Инструменты Pairwise Testing
PICT (Pairwise Independent Combinatorial Testing)
PICT от Microsoft - наиболее широко используемый инструмент pairwise testing.
Базовое Использование
# Создать файл модели: config.txt
Browser: Chrome, Firefox, Edge
OS: Windows, Mac, Linux
Network: WiFi, 4G, Ethernet
# Сгенерировать pairwise тестовые случаи
pict config.txt
Вывод:
Browser OS Network
Chrome Windows WiFi
Chrome Mac 4G
Chrome Linux Ethernet
Firefox Windows Ethernet
Firefox Mac WiFi
Firefox Linux 4G
Edge Windows 4G
Edge Mac Ethernet
Edge Linux WiFi
Продвинутые Возможности PICT
Constraints: Исключить недопустимые комбинации
# model.txt
OS: Windows, Mac, Linux
Browser: IE, Safari, Chrome, Firefox
Resolution: 1024x768, 1920x1080, 2560x1440
# Ограничения
IF [OS] = "Mac" THEN [Browser] <> "IE";
IF [OS] = "Windows" THEN [Browser] <> "Safari";
IF [Resolution] = "2560x1440" THEN [OS] <> "Windows";
Submodels: Увеличить силу взаимодействия для конкретных параметров
# Протестировать все 3-way взаимодействия для критических параметров
Payment: CreditCard, PayPal, Crypto
Currency: USD, EUR, GBP
Amount: Low, Medium, High
{ Payment, Currency, Amount } @ 3
Seeding: Включить конкретные обязательные тестовые случаи
Browser: Chrome, Firefox, Safari
OS: Windows, Mac, Linux
# Засеять конкретные комбинации
Browser OS
Chrome Windows
Firefox Linux
AllPairs (Python)
Чистая Python реализация для программной генерации тестов.
from allpairspy import AllPairs
parameters = [
["Windows", "Mac", "Linux"], # OS
["Chrome", "Firefox", "Safari"], # Browser
["WiFi", "Ethernet", "4G"] # Network
]
# Сгенерировать pairwise комбинации
for i, test in enumerate(AllPairs(parameters)):
print(f"Test {i+1}: OS={test[0]}, Browser={test[1]}, Network={test[2]}")
Вывод:
Test 1: OS=Windows, Browser=Chrome, Network=WiFi
Test 2: OS=Windows, Browser=Firefox, Network=Ethernet
Test 3: OS=Windows, Browser=Safari, Network=4G
Test 4: OS=Mac, Browser=Chrome, Network=Ethernet
Test 5: OS=Mac, Browser=Firefox, Network=4G
Test 6: OS=Mac, Browser=Safari, Network=WiFi
Test 7: OS=Linux, Browser=Chrome, Network=4G
Test 8: OS=Linux, Browser=Firefox, Network=WiFi
Test 9: OS=Linux, Browser=Safari, Network=Ethernet
TestCoverageOptimizer (Java)
import com.pairwise.TCO;
public class PairwiseExample {
public static void main(String[] args) {
// Определить параметры
String[][] parameters = {
{"Windows", "Mac", "Linux"},
{"Chrome", "Firefox", "Edge"},
{"WiFi", "Ethernet"}
};
// Сгенерировать pairwise тесты
TCO tco = new TCO(parameters);
List<int[]> tests = tco.generatePairwise();
// Вывести тестовые случаи
for (int[] test : tests) {
System.out.println(Arrays.toString(test));
}
}
}
CTE (Classification Tree Editor)
GUI инструмент для моделирования и генерации комбинаторных тестов с визуальными деревьями классификации.
Возможности:
- Визуальное моделирование параметров на основе дерева
- Спецификация ограничений
- Множественные критерии покрытия (pairwise, 3-way, и т.д.)
- Экспорт тестовых случаев в различные форматы
Ортогональные Массивы
Ортогональные массивы - это математические структуры, которые гарантируют pairwise покрытие.
Что такое Ортогональные Массивы?
Ортогональный массив OA(N, k, v, t) - это N × k матрица, где:
- N = число тестовых случаев
- k = число параметров
- v = число значений на параметр
- t = сила взаимодействия (2 для pairwise)
Свойство: Каждая N × t подматрица содержит все возможные t-кортежи одинаковое количество раз.
Пример: Массив L9(3^4)
Классический ортогональный массив для 4 параметров с 3 значениями каждый:
Test | P1 | P2 | P3 | P4 |
---|---|---|---|---|
1 | 1 | 1 | 1 | 1 |
2 | 1 | 2 | 2 | 2 |
3 | 1 | 3 | 3 | 3 |
4 | 2 | 1 | 2 | 3 |
5 | 2 | 2 | 3 | 1 |
6 | 2 | 3 | 1 | 2 |
7 | 3 | 1 | 3 | 2 |
8 | 3 | 2 | 1 | 3 |
9 | 3 | 3 | 2 | 1 |
Исчерпывающий потребовал бы 3^4 = 81 теста; ортогональный массив нуждается только в 9.
Применение Ортогональных Массивов
# Отобразить значения на ортогональный массив
parameters = {
'Browser': ['Chrome', 'Firefox', 'Edge'],
'OS': ['Windows', 'Mac', 'Linux'],
'Network': ['WiFi', '4G', 'Ethernet'],
'Theme': ['Light', 'Dark', 'Auto']
}
# Использовать структуру массива L9
L9_array = [
[0, 0, 0, 0],
[0, 1, 1, 1],
[0, 2, 2, 2],
[1, 0, 1, 2],
[1, 1, 2, 0],
[1, 2, 0, 1],
[2, 0, 2, 1],
[2, 1, 0, 2],
[2, 2, 1, 0]
]
# Отобразить на реальные значения
param_names = ['Browser', 'OS', 'Network', 'Theme']
param_values = [
['Chrome', 'Firefox', 'Edge'],
['Windows', 'Mac', 'Linux'],
['WiFi', '4G', 'Ethernet'],
['Light', 'Dark', 'Auto']
]
tests = []
for row in L9_array:
test = {}
for i, param in enumerate(param_names):
test[param] = param_values[i][row[i]]
tests.append(test)
N-Way Testing: За Пределами Pairwise
Хотя pairwise (2-way) наиболее распространен, взаимодействия более высокого порядка могут быть необходимы для критических систем.
Сравнение Силы Взаимодействия
Сила | Название | Покрытие | Тестовые Случаи | Случай Использования |
---|---|---|---|---|
1-way | Each Choice | ~50% | Минимум | Smoke тесты |
2-way | Pairwise | ~70-90% | Малый | Большинство систем |
3-way | Three-way | ~90-95% | Средний | Критические системы |
4-way | Four-way | ~95-99% | Большой | Safety-critical |
All | Исчерпывающий | 100% | Экспоненциальный | Редкий |
Пример 3-Way с PICT
# model.txt
Database: MySQL, PostgreSQL, Oracle
Cache: Redis, Memcached
LoadBalancer: Nginx, HAProxy
# Сгенерировать 3-way взаимодействия
pict model.txt /o:3
Результат: Все 3-параметровые комбинации покрыты (вместо только пар).
Ограничения и Недопустимые Комбинации
Реальные системы имеют недопустимые комбинации, которые должны быть исключены.
Синтаксис Constraint PICT
# E-commerce модель
PaymentMethod: CreditCard, PayPal, BankTransfer, Cash
ShippingSpeed: Standard, Express, Overnight
Country: USA, Canada, Mexico, UK
Amount: $10, $100, $1000
# Ограничения
IF [PaymentMethod] = "Cash" THEN [ShippingSpeed] = "Standard";
IF [Country] = "UK" THEN [PaymentMethod] <> "Cash";
IF [Amount] = "$1000" THEN [PaymentMethod] <> "Cash";
IF [ShippingSpeed] = "Overnight" THEN [Country] <> "Mexico";
Функция Filter AllPairs
from allpairspy import AllPairs
def is_valid_combination(values, names):
payment, shipping, country = values
# Наличные только со стандартной доставкой
if payment == "Cash" and shipping != "Standard":
return False
# UK не принимает наличные
if country == "UK" and payment == "Cash":
return False
return True
parameters = [
["CreditCard", "PayPal", "Cash"],
["Standard", "Express", "Overnight"],
["USA", "UK", "Mexico"]
]
for test in AllPairs(parameters, filter_func=is_valid_combination):
print(test)
Рабочий Процесс Практической Реализации
Шаг 1: Идентифицировать Параметры и Значения
Проанализировать тестируемую систему и перечислить все переменные параметры.
# parameters.yaml
authentication:
- username_password
- oauth
- saml
- api_key
database_backend:
- mysql
- postgresql
- sqlite
caching:
- enabled
- disabled
logging_level:
- debug
- info
- warning
- error
Шаг 2: Определить Ограничения
Документировать недопустимые комбинации на основе бизнес-правил и технических ограничений.
# constraints.py
def validate_config(auth, db, cache, log_level):
# SQLite не поддерживает определенные методы auth
if db == "sqlite" and auth == "saml":
return False
# Debug logging требует отключенного кеширования для точности
if log_level == "debug" and cache == "enabled":
return False
return True
Шаг 3: Сгенерировать Тестовый Набор
Использовать PICT или AllPairs для генерации оптимизированного тестового набора.
# Преобразовать в формат PICT
authentication: username_password, oauth, saml, api_key
database_backend: mysql, postgresql, sqlite
caching: enabled, disabled
logging_level: debug, info, warning, error
IF [database_backend] = "sqlite" THEN [authentication] <> "saml";
IF [logging_level] = "debug" THEN [caching] = "disabled";
# Сгенерировать
pict config.pict > testcases.txt
Шаг 4: Выполнить Тесты
Отобразить сгенерированные комбинации на исполняемые тесты.
import pytest
from allpairspy import AllPairs
@pytest.mark.parametrize("auth,db,cache,log", [
test for test in AllPairs([
["username_password", "oauth", "saml", "api_key"],
["mysql", "postgresql", "sqlite"],
["enabled", "disabled"],
["debug", "info", "warning", "error"]
])
])
def test_configuration(auth, db, cache, log):
# Настроить систему с данной конфигурацией
config = {
'authentication': auth,
'database': db,
'caching': cache,
'logging_level': log
}
system = SystemUnderTest(config)
assert system.health_check()
Преимущества Pairwise Testing
Драматическое Сокращение Тестового Набора
Сократить сотни или тысячи тестов до десятков.
Пример из Практики: Тестирование конфигурации телекома сокращено с 1,200 исчерпывающих тестов до 87 pairwise тестов (93% сокращение) без потери в обнаружении дефектов.
Высокая Скорость Обнаружения Дефектов
Исследования показывают, что pairwise testing ловит 70-90% дефектов, которые нашло бы исчерпывающее тестирование.
Исследование NIST: Анализ реальных сбоев программного обеспечения обнаружил:
- 70% вызвано одиночными параметрами
- 93% вызвано 2-way взаимодействиями
- 98% вызвано 3-way взаимодействиями
Более Быстрое Выполнение
Меньше тестов означает более быстрые циклы обратной связи.
Пример ROI: Время выполнения тестового набора конфигурации сокращено с 8 часов до 45 минут.
Лучшее Обслуживание Тестов
Меньшие тестовые наборы легче поддерживать и обновлять.
Вызовы и Ограничения
Идентификация Полных Наборов Параметров
Пропущенные параметры означают пропущенные взаимодействия.
Смягчение: Провести тщательный анализ с экспертами предметной области; использовать исследовательское тестирование для дополнения.
Сложность Ограничений
Сложные бизнес-правила делают спецификацию ограничений сложной.
Смягчение: Начать с простых ограничений; уточнять итеративно на основе сбоев тестов.
Взаимодействия Более Высокого Порядка
Pairwise не ловит дефекты, требующие взаимодействия 3+ параметров.
Смягчение: Использовать 3-way тестирование для критических модулей; комбинировать с тестированием на основе рисков.
Нефункциональные Аспекты
Pairwise фокусируется на функциональных комбинациях, не на производительности или безопасности.
Смягчение: Комбинировать с выделенным нефункциональным тестированием.
Лучшие Практики
Начинать с Критических Параметров
Фокусировать pairwise на высокорисковых, часто используемых функциях сначала.
# Критический поток платежей
PaymentGateway: Stripe, PayPal, Square
Currency: USD, EUR, GBP
PaymentType: OneTime, Subscription
3DS: Enabled, Disabled
# Некритические UI предпочтения тестируются исчерпывающе (меньше комбинаций)
Theme: Light, Dark
Language: EN, ES
Комбинировать с Equivalence Partitioning
Использовать классы эквивалентности для определения значений параметров.
# Вместо конкретных значений
amounts = [0.01, 50.00, 99.99, 100.00, 1000.00, 9999.99]
# Использовать классы эквивалентности
amounts = ["Minimum", "Standard", "High", "Maximum"]
# Отобразить на представительные значения во время выполнения
Документировать Предположения
Записывать почему существуют ограничения для будущего обслуживания.
# model.pict
Browser: Chrome, Firefox, Safari, IE
OS: Windows, Mac, Linux
# IE работает только на Windows (техническое ограничение)
IF [Browser] = "IE" THEN [OS] = "Windows";
# Safari в основном Mac (бизнес-решение о депр-и Windows Safari)
IF [Browser] = "Safari" THEN [OS] = "Mac";
Контроль Версий Тестовых Моделей
Обращаться с PICT/AllPairs моделями как с кодовыми артефактами.
# Структура Git репозитория
tests/
├── models/
│ ├── authentication.pict
│ ├── checkout.pict
│ └── reporting.pict
├── generated/
│ ├── auth_tests.csv
│ └── checkout_tests.csv
└── scripts/
└── generate_all.sh
Применение в Реальном Мире
Матрица Совместимости Browser/OS
Тестирование веб-приложения через 5 браузеров × 3 OS × 3 размера экрана = 45 исчерпывающих тестов.
Pairwise: 12 тестов, покрывающих все pairwise взаимодействия.
Результаты: Обнаружено 8 проблем рендеринга, все пойманы pairwise набором.
Тестирование Конфигурации API
REST API с 8 параметрами конфигурации, каждый с 2-4 опциями (1,536 исчерпывающих комбинаций).
Pairwise: 24 теста.
Результаты: Найдены конфликты конфигурации, которые были бы упущены случайной выборкой.
Тестирование Фрагментации Устройств
Мобильное приложение, протестированное через версии Android, производители, размеры экрана, сетевые условия.
3-way тестирование: 150 тестов (против 10,000+ исчерпывающих).
Результаты: 95% скорость обнаружения дефектов с 98.5% сокращением времени выполнения тестов.
Заключение
Pairwise testing обеспечивает оптимальный баланс между покрытием тестами и размером тестового набора. Эксплуатируя эмпирическое наблюдение, что большинство дефектов вовлекают взаимодействия нескольких параметров, pairwise testing достигает почти исчерпывающего обнаружения дефектов с долей усилий.
Успех с pairwise testing требует тщательного выбора параметров, продуманного моделирования ограничений и комбинирования pairwise с другими стратегиями тестирования (исследовательское, основанное на рисках, и т.д.) для всестороннего обеспечения качества.