За пределами покрытия решений

В предыдущем уроке вы узнали о покрытии операторов и решений. Покрытие решений гарантирует выполнение каждой ветви, но не показывает, действительно ли тестируются отдельные условия внутри составного решения. Рассмотрим код:

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:

ТестABРешение
1TFF
2FTF

Это обеспечивает 100% покрытие условий — и A, и B принимают значения True и False. Но заметьте: результат решения False в обоих случаях. Мы никогда не протестировали ветвь True. Покрытие условий само по себе может пропустить целые ветви.

Покрытие ветвей/условий (Branch/Condition Coverage)

Покрытие ветвей/условий объединяет оба требования: каждое условие должно принять оба значения И каждая ветвь должна быть выполнена.

Для A AND B:

ТестABРешение
1TTT
2FFF

Теперь покрыты обе ветви и оба условия принимают True и False. Но пробел остаётся — мы не можем определить, влияет ли A независимо на результат, потому что A и B всегда меняются вместе.

Покрытие множественных условий

Покрытие множественных условий (MCC) требует тестирования каждой возможной комбинации значений условий. Для A AND B это:

ТестABРешение
1TTT
2TFF
3FTF
4FFF

Это исчерпывающе, но непрактично для реального кода. При 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 требует:

  1. Каждая точка входа и выхода вызывается (покрытие базовых блоков)
  2. Каждое условие принимает оба значения True и False (покрытие условий)
  3. Каждое решение принимает оба значения True и False (покрытие решений)
  4. Каждое условие независимо влияет на результат решения — для каждого условия существует пара тест-кейсов, где только это условие меняет значение и результат решения меняется

Правило 4 — это то, что делает MC/DC особенным. Оно доказывает, что каждое условие действительно имеет значение.

Вывод тест-кейсов для MC/DC

Для решения A AND B AND C следуйте систематическому процессу:

Шаг 1: Постройте полную таблицу истинности.

#ABCРезультат
1TTTT
2TTFF
3TFTF
4TFFF
5FTTF
6FTFF
7FTFF
8FFFF

Шаг 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:

#ABCA OR BРезультат
1TTTTT
2TTFTF
3TFTTT
4TFFTF
5FTTTT
6FTFTF
7FFTFF
8FFFFF

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

  • 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:

#ABCNOT CA OR BРезультат
1TTFTTT
2TFFTTT
3FTFTTT
4FFFTFF
5TTTFTF
6TFTFTF
7FTTFTF
8FFTFFF

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

  • 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_hightemp_criticalmanual_overrideОжидаемый
1TrueFalseFalseTrue
2FalseTrueFalseTrue
3FalseFalseFalseFalse
4TrueFalseTrueFalse

Задача 2

Система управления полётом содержит логику:

if (altitude_low && airspeed_low && !landing_gear_deployed) {
    trigger_terrain_warning();
}
  1. Сколько тест-кейсов требует MC/DC?
  2. Сколько требует покрытие множественных условий?
  3. Выведите набор тестов MC/DC.
Решение

Решение: A AND B AND NOT C, где A=altitude_low, B=airspeed_low, C=landing_gear_deployed.

  1. MC/DC требует 4 тест-кейса (N+1 = 3+1, так как NOT C — это всё ещё одно условие)
  2. Покрытие множественных условий требует 8 тест-кейсов (2^3)

Таблица истинности:

#ABCNOT CРезультат
1TTFTT
2TTTFF
3TFFTF
4FTFTF

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

  • 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 для сложных решений реального мира