Wprowadzenie do testów mutacji
Dave Aronson przedstawił na JSConf Hawaii 2020 uzasadnienie testowania mutacji, jego zalety, wady i pułapki oraz sposób, w jaki to w ogóle działa.
Aronson rozpoczął od wyjaśnienia, że testowanie mutacji jest sposobem testowania zarówno kodu, jak i pasującego zestawu testów jednostkowych poprzez zmianę (czyli właśnie mutację) testowanego kodu. Typowy test oparty na przykładzie gwarantuje poprawne zachowanie testowanej funkcji tylko dla tego przykładu. Testerzy używają zatem próbki a wnioski wyciągane na podstawie tej próbki rozciągają się na znacznie większą przestrzeń (np. zakres danych). Dla każdego testu w przestrzeni testowej należy również opracować metodę walidacji lub unieważnienia zaobserwowanych wyników testu.
Działanie testu mutacyjnego
Narzędzia do testowania mutacji zazwyczaj działają poprzez znajdowanie funkcji i odpowiadających im testów. Kod funkcji jest następnie analizowany w abstrakcyjnym drzewie składni. Mutacje są stosowane na poziomie drzewa składni, a kod funkcji jest następnie ponownie generowany ze zmutowanego drzewa składni. Przykład? W przypadku warunków warunkowych granice mogą zostać zmienione (z <
na <=
, ==
do !=
lub z if (warunek)
na if (true)
. Przyrosty (++
) można zamienić na ubytki (-
) itd. Niektóre wywołania metod lub konstruktorów mogą zostać unieważnione.
Testowanie mutacji sprawdza więc wiarygodność wybranego zestawu testów, tj. jakość kodu testowego. Jest to niejako test dla testu. Począwszy od sytuacji, w której wszystkie testy przechodzą pomyślnie, testowany kod jest modyfikowany, a zestaw testów jest powtarzany. Jeśli zestaw testów jest niezawodny, powinien wychwycić błędne implementacje testowanej funkcji, a wykonanie zestawu powinno zakończyć się niepowodzeniem.
Testerzy stają przed wyzwaniem, które prof. Tsong Yueh Chen nazwał problemem wyroczni.
Problem wyroczni
Problem wyroczni odnosi się do sytuacji, w których zweryfikowanie wyniku testu danego przypadku testowego jest niezwykle trudne lub niemożliwe. Problem z niezawodnym zestawem testów oznacza, że ponieważ zwykle nie jest możliwe wyczerpujące wykonanie wszystkich możliwych przypadków testowych, trudno jest skutecznie wybrać podzbiór przypadków testowych (wiarygodny zestaw testów) z możliwością określenia poprawności programu.
Testowanie mutacji jest zatem techniką opartą na błędach, która stara się wytworzyć błędne implementacje funkcji poprzez wstawianie błędów do kodu źródłowego. Aronson podał tu przykład narzędzia odpornościowego Chaos Monkey w Netflix, które generuje losowe awarie instancji w celu przetestowania stabilności systemu i wykrycia błędów w odzyskiwaniu błędów.
Wady testowania mutacyjnego
Po pierwsze, testowanie mutacji obciąża procesor i generalnie nie jest przeznaczone do uruchamiania całej bazy kodu przy każdym zapisie - narzędzia zwykle oferują tryb przyrostowy jako środek łagodzący ten problem.
Po drugie, testowanie mutacji nie jest techniką przyjazną dla początkujących. Testy, które nadal przechodzą pomyślnie, nawet jeśli testowany kod został zmutowany, wymagają starannej interpretacji. Wynik pozytywny jest generalnie fałszywie dodatni, co oznacza, że bieżący zestaw testów może nie być wystarczająco wiarygodny (tym samym możemy mówić de facto o braku testów). Może się również zdarzyć, że część samych testów jest błędna.