Co to jest testowanie mutacji? (Przykład)

⚡ Inteligentne podsumowanie

Testowanie mutacyjne polega na celowym wprowadzaniu niewielkich błędów do kodu źródłowego, a następnie uruchamianiu istniejącego zestawu testów dla każdej wadliwej wersji, sprawdzając, czy testy są wystarczająco silne, aby wykryć zmianę.

  • 🔘 Definicja: Mutant to program, który wprowadza jedną celową zmianę składniową, a jego usunięcie dowodzi, że test wykrył tę zmianę.
  • Proces: Generuj mutanty, uruchamiaj pakiet względem oryginału i mutanta, porównuj wyniki, a następnie wzmacniaj testy, które nie wykryły błędów.
  • Operatory: Operaa zastąpienie, modyfikacja ekspresji i modyfikacja instrukcji powodują powstanie trzech głównych rodzin mutantów.
  • 🧪 Wynik: Wynik mutacji to procent zabitych mutantów i mierzy siłę twierdzenia, a nie tylko wykonanie linii.
  • 🛠️. Obróbka: Pokrywy Stryker Javascenariusz, TypeScript, C# i Scala, podczas gdy PIT modyfikuje bajtkod JVM w Maven i Gradle buduje.
  • ⚠️ Koszty: Każdy mutant powtarza cały zestaw testów, więc bez automatyzacji testowanie mutacji jest powolne, kosztowne i niepraktyczne.

Testy mutacji

Co to jest testowanie mutacji?

Testy mutacji Testowanie mutacyjne to rodzaj testowania oprogramowania, w którym pewne fragmenty kodu źródłowego są modyfikowane (mutowane) w celu sprawdzenia, czy przypadki testowe są w stanie wykryć błędy w kodzie źródłowym. Celem testowania mutacyjnego jest zapewnienie jakości przypadków testowych pod względem odporności, tak aby nie dały one rezultatu w przypadku zmutowanego kodu źródłowego.

Zmiana wprowadzona w programie mutacyjnym musi być minimalna, aby nie wpływała na ogólny cel programu. Testowanie mutacyjne nazywane jest również strategią testowania opartą na błędach, ponieważ polega na celowym tworzeniu błędu w programie. Jest to forma… Biały Box Testy który jest stosowany głównie podczas Testów jednostkowych.

Testowanie mutacji zostało zaproponowane w 1971 roku w pracy studenckiej Richarda Liptona i sformalizowane w artykule z 1978 roku „Hints on Test Data Selection” autorstwa DeMillo, Liptona i Saywarda. Straciło ono popularność ze względu na ówczesne koszty obliczeniowe i od tego czasu zyskało popularność w takich językach jak: Java, C#, Python, JavaSkrypt i XML.

Jak przeprowadzić test mutacji?

Poniżej przedstawiono kroki umożliwiające przeprowadzenie testu mutacji, znanego również jako analiza mutacji:

Krok 1: Błędy wprowadza się do kodu źródłowego programu poprzez tworzenie wielu wersji zwanych mutantami. Każdy mutant powinien zawierać jeden błąd, a celem jest spowodowanie awarii wersji zmutowanej, co demonstruje skuteczność przypadków testowych.

Krok 2: Przypadki testowe stosuje się zarówno do programu oryginalnego, jak i do programu zmutowanego. Przypadek testowy powinien być odpowiedni i jest dostosowany do wykrywania błędów w programie.

Krok 3: Porównaj wyniki programu oryginalnego i zmutowanego.

Krok 4: Jeśli program oryginalny i program zmutowany generują różne wyniki, to mutant zostaje usunięty przez przypadek testowy. Zatem przypadek testowy jest wystarczająco dobry, aby wykryć różnicę między programem oryginalnym a zmutowanym.

Krok 5: Jeśli program oryginalny i program mutant generują ten sam wynik, mutant jest utrzymywany przy życiu. W takich przypadkach konieczne jest stworzenie bardziej efektywnych przypadków testowych, które usuną wszystkie mutanty.

Poniższy diagram tracto te same pięć kroków – od oryginalnego programu, poprzez generowanie mutantów, aż do werdyktu o zabiciu lub przeżyciu.

Przebieg pracy testowania mutacji pokazujący oryginalny program, wygenerowane mutanty, wykonanie testu oraz werdykt dotyczący zabitego lub żywego mutanta

Jak tworzyć zmutowane programy?

Mutacja to nic innego jak pojedyncza zmiana składniowa wprowadzona do instrukcji programu. Każdy program zmutowany powinien różnić się od programu oryginalnego dokładnie jedną mutacją.

Oryginalny program Program mutantów
Jeśli (x>y)
Wydrukuj „Witam”
Więcej
Drukuj „Cześć”
Jeśli (x
Wydrukuj „Witam”
Więcej
Drukuj „Cześć”

W powyższej parze zmienił się tylko operator porównania, ale przypadek testowy, w którym x jest większe od y, teraz wyświetla „Hi” zamiast „Hello”. Ilustracja pokazuje tę jedną zmianę składniową.

Jedna zmiana składniowa zastosowana do instrukcji programu w celu wygenerowania pojedynczego mutanta

Co zmienić w programie Mutant?

Istnieje kilka technik, które można wykorzystać do generowania programów mutantów. Trzy poniższe rodziny obejmują większość operatorów mutacji, z którymi dostarczane są narzędzia.

Operaoperatorzy zastępczy Operatorzy modyfikacji wyrażeń Operatorzy modyfikacji instrukcji
Zastąp operand innym operandem (x na y lub y na x) lub stałą wartością. Zastąp operator lub wstaw nowy operator w poleceniu programu. Instrukcje programowe są modyfikowane w celu utworzenia zmutowanych programów.
Przykład:
Jeśli(x>y) zamień wartości x i y
Jeśli(5>y) zamień x na stałą 5
Przykład:
Jeśli(x==y)
Możemy zastąpić == przez >= i mieć program mutanta jako
If(x>=y) i wstawienie ++ w instrukcji
Jeśli(x==++y)
Przykład:
Usuń część else w instrukcji if-else
Usuń całe polecenie if-else, aby sprawdzić, jak zachowuje się program

Przykładowe operatory mutacji:

  • Wymiana etykiety GOTO
  • Wymiana wyciągu zwrotnego
  • Usunięcie oświadczenia
  • Wstawianie operatora jednoargumentowego (takiego jak – i ++)
  • Wymiana złącza logicznego
  • Porównywalna zamiana nazw tablic
  • Usuwanie części else z instrukcji if-else
  • Dodawanie lub zastępowanie operatorów
  • Zastąpienie wyciągu poprzez zmianę danych
  • Modyfikacja danych dla zmiennych
  • Modyfikacja typów danych w programie

Operamutacje, które spełniają warunek brzegowy, przeżywają najczęściej, więc wyniki mutacji często wskazują na luki w analiza wartości brzegowych.

Rodzaje testów mutacji

In Inżynieria oprogramowaniaTestowanie mutacji można zasadniczo podzielić na trzy typy — mutację instrukcji, mutację wartości i mutację decyzji.

  • Mutacja wypowiedzi – polecenie jest wycinane, wklejane lub usuwane, więc wynikiem może być usunięcie kilku linii kodu.
  • Mutacja wartości – modyfikowane są wartości parametrów podstawowych i stałych, np. zmieniana jest granica pętli lub próg.
  • Mutacja decyzyjna – instrukcje sterujące są zmieniane, na przykład flipping operator relacyjny lub negujący warunek.

Narzędzia grupują swoje operatory pod tymi trzema nagłówkami, więc rodzina, która wygenerowała przetrwałego mutanta, informuje testera, jakiego rodzaju asercji brakuje. Przetrwały mutant decyzyjny zazwyczaj oznacza nieprzetestowaną gałąź, która nakłada się na… testowanie pętli.

Automatyzacja testowania mutacji

Testowanie mutacji jest niezwykle czasochłonne i skomplikowane do wykonania ręcznie, dlatego zaleca się korzystanie z narzędzi automatyzujących, które również obniżają koszty. Narzędzie do analizy mutacji kompiluje mutanty, planuje przebiegi, rejestruje, który mutant został wyeliminowany przez każdy nieudany test, i raportuje wynik.

Lista dostępnych narzędzi:

  • Stryker — otwartoźródłowe środowisko testowania mutacji z edycjami dla JavaSkrypt i TypeScript (StrykerJS), C# i .NET (Stryker.NET) oraz Scala (Stryker4s).
  • PIT, również nazywany PITest — system testowania mutacji dla Java i JVM, która modyfikuje skompilowany kod bajtowy i łączy się z Mavenem i Gradle buduje obok JUnit.

Oba działają jako krok kompilacji, więc należą do tego samego ciągła integracja rurociąg jak reszta testowanie automatyzacji na.

Wynik mutacji

Wynik mutacji definiuje się jako odsetek zabitych mutantów w stosunku do całkowitej liczby mutantów.

Wynik mutacji = (zabite mutanty / całkowita liczba mutantów) * 100

Poniżej przedstawiono wzór w formie, w jakiej zgłasza go większość narzędzi.

Wzór na wynik mutacji polegający na podzieleniu zabitych mutantów przez całkowitą liczbę mutantów i pomnożeniu przez sto

Przypadki testowe określa się jako mutację adekwatną, gdy wynik osiąga 100 procent. W praktyce mianownik musi wykluczać równoważne mutanty — mutanty, których zmieniona składnia zachowuje się dokładnie tak samo jak oryginał, więc żaden test nie może ich usunąć. Dlatego narzędzia raportują liczbę usuniętych mutantów podzieloną przez liczbę usuniętych plus pozostałych przy życiu mutantów nierównoważnych i pozwalają testerowi oznaczyć równoważne mutanty.

Wyniki eksperymentów wykazały, że testowanie mutacji jest skutecznym sposobem pomiaru adekwatności przypadków testowych. Główną wadą jest koszt generowania mutantów i wykonywania każdego przypadku testowego dla każdego z nich.

Testowanie mutacji vs. Code Pokrycie

Wysoki pokrycie testu Nie dowodzi to silnych testów. Pokrycie linii i gałęzi rejestruje, które instrukcje zostały wykonane, a nie to, czy cokolwiek zostało później zweryfikowane, więc test, który wywołuje metodę i nic nie potwierdza, nadal jest uznawany za pokryty. Testowanie mutacji wypełnia tę lukę, ponieważ mutant ginie dopiero wtedy, gdy potwierdzenie faktycznie się nie powiedzie.

WYGLĄD Code pokrycie Wynik mutacji
Co mierzy Które linie lub gałęzie zostały wykonane przez testy Które błędy wtryskiwane wykryły testy
Wrażliwy na twierdzenia Nie — test z zerową liczbą asercji nadal zwiększa zasięg Tak — mutant przeżywa, gdy żadne stwierdzenie nie zawodzi
Koszt biegu Jeden test z instrumentami Jeden test na każdego żyjącego mutanta, jak dotąd wolniejszy
Typowe zastosowanie Szybka brama przy każdym zatwierdzeniu Dokładniejsza okresowa kontrola krytycznych modułów
Tryb awaryjny 100-procentowe pokrycie bez faktycznej weryfikacji Równoważne mutanty, których nigdy nie można zabić

Te dwie metryki się uzupełniają. Pokrycie oznacza kod, do którego nigdy nie osiągnięto; wynik mutacji oznacza kod, do którego nigdy nie sprawdzono. Oba wskaźniki dostarczają tych samych danych. proces zarządzania defektami, obok środków takich jak gęstość defektów.

Zalety testowania mutacji

Oto zalety testów mutacji:

  • Jest to skuteczne podejście pozwalające uzyskać wysoki poziom pokrycia programu źródłowego.
  • Testuje sam zestaw testów, czego nie robi żaden inny technika testowania oprogramowania robi bezpośrednio.
  • Testowanie mutacyjne zapewnia programistom oprogramowania wysoki poziom wykrywania błędów.
  • Metoda ta pozwala na wykrywanie niejednoznaczności w kodzie źródłowym i jest w stanie wykryć błędy, których zwykłe obliczenia nigdy nie wykryją.
  • Ocalałe mutanty są wykonalne: każdy z nich podaje konkretną linię i konkretną zmianę, której zespół nie zauważył.
  • Klienci korzystają z tych testów, otrzymując bardziej niezawodny i stabilny system.

Wady testowania mutacji

Z drugiej strony, testy mutacji mają również następujące wady:

  • Testowanie mutacji jest niezwykle kosztowne i czasochłonne, ponieważ konieczne jest wygenerowanie i skompilowanie dużej liczby programów mutantów.
  • Biorąc pod uwagę, że jest to czasochłonne, można śmiało powiedzieć, że testowanie takie nie może zostać przeprowadzone bez narzędzia automatyzującego.
  • Każdy mutant jest testowany przez taką samą liczbę przypadków testowych, co program oryginalny, więc duża populacja mutantów musi zostać przetestowana w całym zestawie testów.
  • Mutantów równoważnych nie da się zabić żadnym testem, a oddzielenie ich od prawdziwych ocalałych zazwyczaj wymaga ręcznej analizy.
  • Ponieważ metoda ta zmienia kod źródłowy, nie nadaje się do zastosowania Czarny Box Testy.

Kiedy stosować testy mutacji

Powyższy profil kosztów oznacza, że ​​testy mutacyjne rzadko przeprowadza się na całej bazie kodu przy każdym zatwierdzeniu. Opłacają się one, gdy niewykryty błąd jest kosztowny, a testowany kod jest na tyle mały, że szybko ulega mutacji.

  • Krytyczne dla bezpieczeństwa lub logika finansowa — obliczanie płatności, zasady podatkowe i kontrole autoryzacji, gdzie jedna błędna odpowiedź może być gorsza niż awaria.
  • Apartamenty z podejrzanie wysokim zasięgiem — gdy pokrycie wynosi prawie 100 procent, ale nadal pojawiają się defekty.
  • Przebudowa starego kodu — wyniki mutacji ujawniają, czy istniejące testy wykryją regresję.
  • Biblioteki i współdzielone komponenty — usterka w ponownie użytym składnik mnoży się przez każdego rozmówcę.
  • Zespoły ćwiczące rozwój oparty na testach — wyniki sprawdzają, czy testy napisane jako pierwsze rzeczywiście działają.

Zwykle nie warto uruchamiać oprogramowania na prototypach jednorazowych, na cienkim kleju lub generowanym kodzie bez logiki rozgałęzień lub na pakietach zdominowanych przez wolne testy integracyjne które już na jedno przejście zajmują godziny.

Większość zespołów ogranicza zatem zakres uruchomienia do zmienionych plików, ustala próg dla modułów, które mają znaczenie, i pozwala na szersze testy regresji apartament zabierze resztę cykl życia testowania oprogramowania.

FAQ

Równoważny mutant to zmiana, która zmienia składnię, ale nie zachowanie, na przykład zastąpienie granicy pętli, która nigdy nie zostaje osiągnięta. Żaden test nie może go usunąć, dlatego musi zostać oznaczony i wykluczony, zanim wynik zostanie uznany za wiarygodny.

Nie ma uniwersalnej liczby. Zespoły zazwyczaj ustalają wysoki próg dla modułów krytycznych, takich jak płatności czy logika bezpieczeństwa, a niższy dla innych. Pogoń za procentem jest mniej przydatna niż przeglądanie każdego ocalałego mutanta w kodzie wysokiego ryzyka.

To dwa założenia, na których opiera się ta technika. Pierwsze z nich mówi, że programiści piszą niemal poprawny kod, więc rzeczywiste błędy są niewielkie. Drugie zaś mówi, że testy, które wychwytują drobne błędy, wychwytują również złożone błędy z nich wynikające.

Ogranicz mutację do plików zmienionych w bieżącej gałęzi, ponownie wykorzystaj dane o pokryciu, tak aby wykonywane były tylko testy obejmujące mutanta, uruchamiaj mutanty równolegle i nie pozwól na kompilację w przypadku spadku wyniku, a nie wartości bezwzględnej.

Modele uczenia maszynowego przewidują, które mutanty mają największe szanse na przetrwanie, dzięki czemu można przyciąć przebieg, klasyfikować prawdopodobnie równoważne mutanty do przeglądu i generować mutanty, które przypominają błędy widoczne w historii projektu, a nie jednorodne zamiany operatorów.

Tak, jeśli chodzi o część mechaniczną. Biorąc pod uwagę ocalałego mutanta i testowaną metodę, Copilot szkicuje brakującą asercję lub test przypadku skrajnego. Recenzent nadal musi potwierdzić, że wartość oczekiwana jest poprawna, a nie po prostu skopiowana z bieżącego zachowania.

Audytuje wynik. W programowaniu sterowanym testami (TDD) testy są generowane przed kodem, ale nic nie gwarantuje, że testy te będą wystarczająco dokładne. Okresowe uruchamianie mutacji w tych samych modułach pokazuje, czy cykl czerwono-zielony wygenerował testy, które rzeczywiście kończą się niepowodzeniem z powodu błędnej odpowiedzi.

Nie. Wstrzyknięcie błędu powoduje uszkodzenie środowiska wykonawczego i testowanie rozmycia Podaje nieprawidłowe dane wejściowe, oceniając jednocześnie aplikację. Testowanie mutacyjne zmienia kod źródłowy i ocenia zestaw testowy, więc obiekt poddawany ocenie jest inny.

Podsumuj ten post następująco: