Comments 16
А оставшаяся часть подобного тестирования, а именно — получив ошибку на каком-то случайном наборе данных, пытаемся упростить набор данных,
до тех пор пока ошибка воспроизводится, реализована в каком-нибудь
проекте для C++
?
Я реализовал такое, но на C99. Даже почти довел до первого релиза, думал статью на хабре написать, но в какой-то момент очень резко кончилось свободное время. Поэтому похоже вместо статьи будет этот коммент.
Основной целью было сделать тестовый фреймворк, в котором максимально просто писать тесты, которым можно тестировать plain C код, и для работы которого не обязательно наличие системного malloc. Насколько получилось — можно посмотреть тут. К сожалению пока там есть места с говнокодом, с неконсистентыми именами (с этим вообще беда, иногда подолгу метался между разными вариантами) и не все покрыто тестами — но оно поэтому и не релиз пока что. Зато вроде получилось действительно просто писать тесты (тестирую фреймворк самим собой), и реализовано упрощение тестовых наборов при падении теста. Ну и если кто-то будет смотреть — хотелось бы обратной связи — оно вообще в таком виде кому-то надо/интересно? Стоит ли продолжать работу?
В текущем проекте есть нечто подобное, привязанное к boost::fusion и rttr (можно так же добавить boost::hana и magic_get) и умеющее генерировать рандомные объекты pod-типов :) для тестов различных сериализаций / десериализаций / orm-ов / и т.п. отлично подходит)
Тестирование на рандомных объектах немного антипаттерн, тесты будут то фейлится то нет.
Здесь играет роль размер выборки, ведь можно сгенерировать несколько миллионов случайных
вариантов. Плюс главное чтобы хоть раз зафейлился, потом берется данные на которых
зафейлился и после починки добавляются в тест напрямую в качестве дополнения к генерируемых случайным
образом. Плюс конечно очевидные крайние случаи стоит тестировать напрямую, типа пустое значение, максимальное значение и т.д.
int Foo(int num, int div)
{
return num / div;
}
Здесь играет роль размер выборки, ведь можно сгенерировать несколько миллионов случайных
вариантов.
Как эти несколько миллионов рандомных вариантов протестируют эту функцию? Подсказка, инпут позволяет 4.6e18 вариантов. Даже 100 миллионов вариантов не покроет и 1%.
Это желание переложить создание тесткейсов на рандомный объект на практике будет означать что код будет протестирован всеголишь чуточку лучше чем smoke test, то есть практически никак.
Как эти несколько миллионов рандомных вариантов протестируют эту функцию?
Даже 100 миллионов вариантов не покроет и 1%.
100 миллиардов в данном случае помогут, т.к. 2^32=4_294_967_296
,
т.е. у нас неминуемого будет 0
во втором аргументе, т.е. тест будет падать при каждом
прогоне.
Это желание переложить создание тесткейсов на рандомный объект на практике будет означать что код будет протестирован всеголишь чуточку лучше чем smoke test, то есть практически никак.
Соглашусь с неявно витающей мыслью — это не серебренная пуля,
и неким магическим образом она не даст 100% покрытие.
Не соглашусь, что она только чуть-чуть улучшит smoke test.
Давайте изменим методику подсчета :)
Пользуясь эмпирическим опытом выскажу гипотезу, что в обычном проекте (т.е. от которого
не зависит жизнь или огромные суммы денег), данная функция будет максимум
протестирована для 1000 вариантов (хотя скорее максимум для 3 :) ),
т.е. 100_000_000
, увеличит вероятность нахождения
ошибки в 100_000 раз. Да, общая вероятность увеличится незначительно,
но ведь это инструмент, никто не мешает применять его с умом:
int test_Foo_special(uint8_t a, uint8_t b)
{
static const int SPECIAL_VALUES[] = {
INT_MAX,
INT_MIN,
0,
INT_MAX / 2,
INT_MIN / 2,
};
return Foo(SPECIAL_VALUES[a], SPECIAL_VALUES[b]);
}
скорее всего уже эта test_Foo_special
на практике позволит найти 90% ошибок,
хотя казалось бы общее количество тестируемых вариантов увеличилось незначительно.
Т.е. резюмируя по сравнению с тем, что ленивый программист пишет, позволяет увеличить
тестовое покрытие в миллионы раз, и но конечно в абсолютном значении процент
вариантов входных данных для которых проведено тестирование, вырастет незначительно,
поэтому надо применять с умом и других инструментов/специально обученных людей подобные
библиотеки не заменят.
В нормальных фреймворках для property-based тестирования случайности не совсем случайны. Например, если требуется какой-то int, то хорошим тоном будет считаться существенно более частое выпадение значений в районе 0, INT_MIN и INT_MAX. И шансы, что тест этой функции зафейлится даже на 10 тестах уже довольно высоки. А итераций без проблем может быть и 1000.
Генератор тестовых данных для C++