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

Подписаться

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

Конференции

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

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

Про инструменты

Лучшие вакансии

.
Метаморфическое тестирование: почему об этой перспективной методике почти никто не знает
26.07.2019 00:00

imageАвтор оригинала: Hillel Wayne

Оригинальная публикация
Перевод статьи

Должен признаться: я читаю ACM Magazine. Это делает меня «ботаником» даже по меркам программистов. Среди прочего, я узнал из этого журнала о «метаморфическом тестировании». Раньше я никогда о нём не слышал, как и все люди, которых я спрашивал. Но научная литература по этой теме на удивление объёмна: есть множество невероятно успешных примеров её применения в совершенно разных областях исследований. Так почему же мы не слышали о нём раньше? Существует только одна статья для людей вне научных кругов. Пусть теперь их будет две.


Краткая предыстория

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

def test_dist():
    p1 = (0, 3)
    p2 = (4, 0)
    assert dist(p1, p2) == 5

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

Это приводит нас к генеративному тестированию (generative testing): написанию тестов, покрывающих случайное множество в пространстве состояний. Самым популярным стилем генеративного тестирования является property based testing, или PBT. Мы находим «свойство» (property) функции, а затем генерируем входные значения и проверяем, соответствуют ли выходные значения этому свойству.

def test_dist():
    p1 = random_point()
    p2 = random_point()
    assert dist(p1, p2) >= 0

Преимущество PBT заключается в покрытии большего пространства. Его недостаток — утеря специфичности. Это уже не оракул-тест! Мы не знаем, каким должен быть ответ, и функция может быть ошибочна, но таким образом, что обладает тем же свойством. Здесь мы полагаемся на эвристики.

Серьёзная проблема PBT — нахождение хороших свойств. У большинства функций есть простые, общие свойства и сложные, специфические свойства. Общие свойства могут применяться к большому множеству функций, но они не дают нам особо много информации. Более специфические свойства дают больше информации, но их сложнее найти и они применимы только в областях ограниченных задач. Если у вас есть функция, определяющая, является ли граф ациклическим, то какие property-тесты вы напишете? Дадут ли они вам уверенность в том, что функция верна?

Мотивация

Теперь рассмотрим более сложную задачу. Представьте, что вы хотите написать преобразователь речи в текст (speech-to-text, STT) для английского языка. Он получает звуковой файл, а выводит текст. Как бы вы его тестировали?

Проще всего использовать ручной оракул. Продиктовать предложение и проверить, совпадает ли с ним текст на выходе. Но этого даже близко недостаточно! Диапазон человеческой речи огромен. Лучше будет протестировать 1 000 или даже 10 000 разных звуковых файлов. Ручные оракулы с транскрипцией были бы слишком затратными. Это значит, что нам придётся вместо них использовать property-based testing.

Но как нам сгенерировать входные данные? Например, мы можем создать случайные строки, пропустить их через преобразователь текста в речь (text-to-speech, TTS), а затем убедиться, что наш STT выдаёт тот же текст. Но это опять же даёт нам очень ограниченный диапазон человеческого голоса. Сможет ли TTS создать изменения интонаций, «глотать» слова, имитировать сильный акцент? Если мы не справимся с ними, то будет ли STT особо полезен? Лучше использовать произвольные тексты, например, записи с радио, из подкастов и онлайн-видео.

Теперь возникает новая проблема. При использовании TTS мы начинали с письменного текста. В случае произвольных звуковых файлов у нас его нет, и в то же время мы не хотим выполнять транскрипцию вручную. Вместо этого мы ограничены использованием свойств. Какие же свойства нам нужно протестировать? Примеры простейших свойств: «программа не вылетает при любых входящих данных» (хорошее свойство) или «она не преобразует в слова акустическую музыку» (возможно?). Эти свойства не очень хорошо покрывают проверку основной задачи программы и слабо повышают уверенность в её качестве.

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

Метаморфическое тестирование

При всём этом выходные данные рассматриваются по отдельности. Что если мы встроим их в более широкий контекст? Например, если звуковой клип транскрибируется в выходные данные out, то мы должны всегда получать out при:

  • Повышении громкости вдвое
  • Повышении частоты
  • Увеличении темпа