За пределами покрытия решений
В предыдущем уроке вы узнали о покрытии операторов и решений. Покрытие решений гарантирует выполнение каждой ветви, но не показывает, действительно ли тестируются отдельные условия внутри составного решения. Рассмотрим код:
if sensor_active and temperature > threshold and not emergency_override:
activate_cooling_system()
Покрытие решений требует только два тест-кейса — один, где всё выражение True, и один, где False. Но какое условие вызвало результат False? Покрытие решений этим не интересуется. Для системы охлаждения на атомной станции это различие критично.
Именно здесь вступают в дело критерии покрытия на уровне условий, вершиной которых является MC/DC — наиболее строгая практическая метрика покрытия в safety-critical индустриях.
Покрытие условий (Condition Coverage)
Покрытие условий требует, чтобы каждое отдельное условие в решении принимало оба значения True и False в рамках тестового набора.
Для решения A AND B:
| Тест | A | B | Решение |
|---|---|---|---|
| 1 | T | F | F |
| 2 | F | T | F |
Это обеспечивает 100% покрытие условий — и A, и B принимают значения True и False. Но заметьте: результат решения False в обоих случаях. Мы никогда не протестировали ветвь True. Покрытие условий само по себе может пропустить целые ветви.
Покрытие ветвей/условий (Branch/Condition Coverage)
Покрытие ветвей/условий объединяет оба требования: каждое условие должно принять оба значения И каждая ветвь должна быть выполнена.
Для A AND B:
| Тест | A | B | Решение |
|---|---|---|---|
| 1 | T | T | T |
| 2 | F | F | F |
Теперь покрыты обе ветви и оба условия принимают True и False. Но пробел остаётся — мы не можем определить, влияет ли A независимо на результат, потому что A и B всегда меняются вместе.
Покрытие множественных условий
Покрытие множественных условий (MCC) требует тестирования каждой возможной комбинации значений условий. Для A AND B это:
| Тест | A | B | Решение |
|---|---|---|---|
| 1 | T | T | T |
| 2 | T | F | F |
| 3 | F | T | F |
| 4 | F | F | F |
Это исчерпывающе, но непрактично для реального кода. При N условиях нужно 2^N тест-кейсов. Решение с 10 условиями требует 1 024 теста. С 20 условиями: более миллиона.
Modified Condition/Decision Coverage (MC/DC)
MC/DC — это практический компромисс. Он был разработан NASA и Boeing в 1990-х для авионики и стал обязательным критерием покрытия для safety-critical авиационного ПО по стандарту DO-178B (сейчас DO-178C).
MC/DC требует:
- Каждая точка входа и выхода вызывается (покрытие базовых блоков)
- Каждое условие принимает оба значения True и False (покрытие условий)
- Каждое решение принимает оба значения True и False (покрытие решений)
- Каждое условие независимо влияет на результат решения — для каждого условия существует пара тест-кейсов, где только это условие меняет значение и результат решения меняется
Правило 4 — это то, что делает MC/DC особенным. Оно доказывает, что каждое условие действительно имеет значение.
Вывод тест-кейсов для MC/DC
Для решения A AND B AND C следуйте систематическому процессу:
Шаг 1: Постройте полную таблицу истинности.
| # | A | B | C | Результат |
|---|---|---|---|---|
| 1 | T | T | T | T |
| 2 | T | T | F | F |
| 3 | T | F | T | F |
| 4 | T | F | F | F |
| 5 | F | T | T | F |
| 6 | F | T | F | F |
| 7 | F | T | F | F |
| 8 | F | F | F | F |
Шаг 2: Найдите пары независимости для каждого условия. Пара независимости для условия X — это две строки, где X меняется, все остальные условия остаются прежними, и результат решения меняется.
- Условие A: Строки 1 (T,T,T→T) и 5 (F,T,T→F) — A меняется, B и C остаются T, результат меняется
- Условие B: Строки 1 (T,T,T→T) и 3 (T,F,T→F) — B меняется, A и C остаются T, результат меняется
- Условие C: Строки 1 (T,T,T→T) и 2 (T,T,F→F) — C меняется, A и B остаются T, результат меняется
Шаг 3: Выберите минимальный набор, покрывающий все пары: строки {1, 2, 3, 5} = 4 тест-кейса.
Для N условий с AND: N+1 тест-кейсов. Для N условий с OR: тоже N+1. Это значительно лучше, чем 2^N для покрытия множественных условий.
MC/DC для смешанных операторов
Когда решение содержит AND и OR одновременно, процесс тот же, но поиск пар независимости требует большей аккуратности.
Для (A OR B) AND C:
| # | A | B | C | A OR B | Результат |
|---|---|---|---|---|---|
| 1 | T | T | T | T | T |
| 2 | T | T | F | T | F |
| 3 | T | F | T | T | T |
| 4 | T | F | F | T | F |
| 5 | F | T | T | T | T |
| 6 | F | T | F | T | F |
| 7 | F | F | T | F | F |
| 8 | F | F | F | F | F |
Пары независимости:
- A: Строки 3 (T,F,T→T) и 7 (F,F,T→F)
- B: Строки 5 (F,T,T→T) и 7 (F,F,T→F)
- C: Строки 1 (T,T,T→T) и 2 (T,T,F→F)
Минимальный набор: {1, 2, 3, 5, 7} = 5 тест-кейсов. Смешанные операторы могут потребовать немного больше N+1.
Где требуется MC/DC
MC/DC — это не академическое упражнение. Он обязателен по реальным стандартам:
| Стандарт | Область | MC/DC требуется для |
|---|---|---|
| DO-178C | Авиация | ПО Level A (катастрофический отказ) |
| ISO 26262 | Автомобили | ПО ASIL D (наивысший риск) |
| IEC 61508 | Промышленность | SIL 4 (наивысший уровень безопасности) |
| EN 50128 | Железные дороги | ПО SIL 3 и SIL 4 |
Если вы работаете в любой из этих отраслей, MC/DC — не опция, а нормативное требование.
Инструменты для анализа MC/DC
Ряд инструментов поддерживает измерение MC/DC:
- VectorCAST — стандарт индустрии для embedded и safety-critical систем
- LDRA — широко используется в аэрокосмической и автомобильной отраслях
- Parasoft C/C++test — поддерживает MC/DC для кодовых баз на C и C++
- gcov + кастомный анализ — open-source вариант для базового отслеживания условий
- JaCoCo — инструмент покрытия для Java, поддерживающий покрытие ветвей (не полный MC/DC, но полезен как приближение)
Упражнение: Вывод тест-кейсов MC/DC
Задача 1
Дана функция:
def should_trigger_alert(pressure_high, temp_critical, manual_override):
return (pressure_high or temp_critical) and not manual_override
Выведите минимальный набор тестов для MC/DC.
Решение
Строим таблицу истинности для (A OR B) AND NOT C, где A=pressure_high, B=temp_critical, C=manual_override:
| # | A | B | C | NOT C | A OR B | Результат |
|---|---|---|---|---|---|---|
| 1 | T | T | F | T | T | T |
| 2 | T | F | F | T | T | T |
| 3 | F | T | F | T | T | T |
| 4 | F | F | F | T | F | F |
| 5 | T | T | T | F | T | F |
| 6 | T | F | T | F | T | F |
| 7 | F | T | T | F | T | F |
| 8 | F | F | T | F | F | F |
Пары независимости:
- A (pressure_high): Строки 2 (T,F,F→T) и 4 (F,F,F→F)
- B (temp_critical): Строки 3 (F,T,F→T) и 4 (F,F,F→F)
- C (manual_override): Строки 2 (T,F,F→T) и 6 (T,F,T→F)
Минимальный набор: {2, 3, 4, 6} = 4 тест-кейса.
| Тест | pressure_high | temp_critical | manual_override | Ожидаемый |
|---|---|---|---|---|
| 1 | True | False | False | True |
| 2 | False | True | False | True |
| 3 | False | False | False | False |
| 4 | True | False | True | False |
Задача 2
Система управления полётом содержит логику:
if (altitude_low && airspeed_low && !landing_gear_deployed) {
trigger_terrain_warning();
}
- Сколько тест-кейсов требует MC/DC?
- Сколько требует покрытие множественных условий?
- Выведите набор тестов MC/DC.
Решение
Решение: A AND B AND NOT C, где A=altitude_low, B=airspeed_low, C=landing_gear_deployed.
- MC/DC требует 4 тест-кейса (N+1 = 3+1, так как NOT C — это всё ещё одно условие)
- Покрытие множественных условий требует 8 тест-кейсов (2^3)
Таблица истинности:
| # | A | B | C | NOT C | Результат |
|---|---|---|---|---|---|
| 1 | T | T | F | T | T |
| 2 | T | T | T | F | F |
| 3 | T | F | F | T | F |
| 4 | F | T | F | T | F |
Пары независимости:
- A: Строки 1 и 4 (A меняется T→F, B=T, C=F фиксированы, результат T→F)
- B: Строки 1 и 3 (B меняется T→F, A=T, C=F фиксированы, результат T→F)
- C: Строки 1 и 2 (C меняется F→T, A=T, B=T фиксированы, результат T→F)
Минимальный набор MC/DC: {1, 2, 3, 4} — ровно 4 теста вместо 8.
Практические советы по MC/DC
Упрощайте перед анализом. Если условие содержит избыточные подвыражения, сначала упростите с помощью булевой алгебры. MC/DC для упрощённого выражения требует меньше тестов.
Связанные и несвязанные условия. Когда два условия используют одну переменную (например, x > 5 AND x < 10), нельзя изменить одно независимо от другого. Это называется связанными условиями. Связанные условия могут требовать других пар независимости, чем несвязанные.
Используйте masking MC/DC для сложных выражений. В masking MC/DC (разрешён DO-178C) можно показать, что условие независимо влияет на решение, даже если другие условия меняются, при условии что изменения «замаскированы» (не влияют на результат через логическую структуру). Это может ещё больше сократить набор тестов.
Вычисление с коротким замыканием имеет значение. В языках с коротким замыканием (большинство современных языков) A AND B не вычисляет B, если A равно False. Это означает, что некоторые тест-кейсы MC/DC могут не исполнять определённые условия в runtime. Учитывайте, обрабатывает ли ваш инструмент покрытия поведение короткого замыкания.
Ключевые выводы
- Покрытие условий тестирует отдельные условия, но может пропустить целые ветви
- Покрытие ветвей/условий объединяет оба подхода, но не доказывает независимость
- MC/DC доказывает, что каждое условие независимо влияет на результат решения
- MC/DC требует N+1 тест-кейсов (против 2^N для покрытия множественных условий) — практично даже для сложных решений
- MC/DC обязателен по DO-178C, ISO 26262 и IEC 61508 для safety-critical ПО
- Всегда ищите пары независимости систематически через таблицы истинности
- Учитывайте связанные условия, короткое замыкание и masking MC/DC для сложных решений реального мира