Back to archive
#ai#java#software-engineering#testing#architecture#agents#verification#aigen

Oczekiwany wynik testu musi być niezależny od kodu agenta

Biblioteka pozwala ustawić źródło filmu jako kod osadzenia albo plik. Jeżeli kod osadzenia został już ustawiony, próba dodania pliku powinna kończyć się wyjątkiem. Model dopisuje jednak warunek, który odrzuca plik tylko wtedy, gdy kod osadzenia jest również niepusty. Test z niepustym tekstem przechodzi. Dla pustego tekstu program zachowuje się inaczej niż oryginalna implementacja. Taki przypadek opisano w badaniu ExCoder.

Można sprawdzić osobno brak kodu osadzenia oraz niepusty tekst i wykonać obie gałęzie tego warunku. Nadal zabraknie przypadku pustego tekstu. Gdyby następnie agent dopisał taki test, wyprowadzając oczekiwany wynik z obecnej implementacji, mógłby utrwalić błąd: test potwierdzałby, że operacja powinna się udać. W repozytorium pojawiłaby się wtedy asercja broniąca zachowania, którego projektant nie zamierzał dopuścić.

Test musi rozróżniać konkurencyjne interpretacje wymagania

W tym przykładzie trzeba rozstrzygnąć, czy pusty kod osadzenia oznacza brak źródła filmu, czy źródło już ustawione. Obie interpretacje da się zaimplementować i pokryć testami. O wyborze powinien decydować kontrakt biblioteki.

Dla zachowania oryginalnej implementacji potrzebujemy następujących przypadków:

Wartość kodu osadzeniaPróba dodania pliku
nullDozwolona
Pusty tekstWyjątek
Niepusty tekstWyjątek

Jeśli wymaganie pozostawia drugi wiersz otwarty, trzeba podjąć decyzję przed zatwierdzeniem asercji. Samo uruchomienie programu pokaże, co implementacja robi z pustym tekstem. Nie odpowie, czy tak miała działać.

To rozróżnienie dotyczy również testów generowanych przez człowieka. Agent pozwala jednak szybko dopisać wiele asercji na podstawie kodu, zanim ktoś sprawdzi ich zgodność z wymaganiem. Dlatego w procesie generowania testów potrzebne jest jawne źródło oczekiwanego zachowania.

Agent może analizować kod, ale potrzebuje niezależnego wzorca wyniku

Przy szukaniu danych testowych dostęp do implementacji jest przydatny. Agent może analizować warunki, sprawdzać pokrycie i szukać wejść ujawniających różnice między wersjami. Osobnej kontroli wymaga sposób ustalenia oczekiwanego wyniku dla znalezionych wejść.

Przy refaktoryzacji wzorcem może być wcześniejsza, zaakceptowana wersja programu. Uruchamiamy na obu wersjach te same dane i porównujemy rezultaty. Jeżeli zmiana ma naprawić znany błąd, właściwe zachowanie w tym obszarze trzeba określić osobno, bo mechaniczne porównanie ze starą wersją wymuszałoby zachowanie usterki.

Dla nowej funkcji takiej implementacji referencyjnej jeszcze nie ma. Oczekiwania mogą pochodzić z kontraktu konsumenta, reguł domenowych albo przykładów zatwierdzonych przez osobę odpowiedzialną za wymagania. Agent może pomóc przełożyć je na testy. Przy przeglądzie asercji musi jednak dać się wskazać wymaganie, z którego wynika każda istotna wartość oczekiwana.

Problem został zmierzony w badaniu obejmującym 6066 wybranych trudniejszych błędów w funkcjach Pythona generowanych przez pięć modeli. Testy dobierane według pokrycia instrukcji, gałęzi lub analizy mutacyjnej wywoływały różnicę względem implementacji referencyjnej średnio dla około 32–39% błędów. Ich asercje wykrywały około 1–2%. Dane wejściowe potrafiły więc ujawnić odmienne zachowanie, którego test nie rozpoznawał jako błędnego. To wynik dla wyselekcjonowanych przypadków z benchmarków, a nie szacunek skuteczności testów w dowolnym projekcie.

Dostarczenie modelowi większej ilości informacji o repozytorium rozwiązuje tylko część problemu. W ExCoderze odtwarzano usuniętą obsługę wyjątków w 304 metodach Java z 75 projektów. Dla Qwen 2.5 Coder 32B dodanie analizy statycznej i danych z wykonania zwiększyło odsetek pierwszych odpowiedzi przechodzących testy autorów z 73,36% do 85,92%. Wśród metod, dla których uzyskano przechodzące rozwiązanie, około jedna czwarta wybranych do oceny odpowiedzi nadal odbiegała od oryginału. Eksperyment dotyczył syntetycznie usuniętego kodu; pokazuje korzyść z analizy programu, ale również ograniczenia odtwarzania zamiaru autora z istniejących testów.

Zmiana asercji powinna pokazywać zmianę wymagań

Wynika z tego propozycja organizacji pracy: razem z testem przechowujmy informację, skąd pochodzi jego oczekiwany wynik. Przy porównaniu wersji będzie to konkretny commit referencyjny. Przy nowym zachowaniu może to być zatwierdzony przypadek testowy lub kontrakt określający wynik operacji. Powiązanie powinno być dostępne podczas przeglądu zmiany.

Agent może proponować poprawki zarówno w implementacji, jak i w kontrakcie. Narzędzia powinny jednak pokazać te zmiany oddzielnie, żeby poprawienie testu po nieudanym uruchomieniu nie oznaczało automatycznie zaakceptowania nowego zachowania. Zmiana oczekiwań wymaga uzasadnienia wynikającego z wymagania.

Przy wyjątkach trzeba też sprawdzać stan po odrzuconej operacji. Poprawny typ wyjątku nie wystarczy, jeżeli metoda wcześniej zdążyła zapisać niedozwolone dane. W przykładzie z filmem test powinien potwierdzić również, że próba dodania pliku pozostawiła obiekt bez zmian.

Taki sposób pracy doprecyzowuje niezależne kryteria akceptacji zmian agentowych. Podczas przeglądu poprawki do walidacji trzeba zobaczyć, jakie dane wcześniej przechodziły, jakie mają być teraz odrzucane i na podstawie którego wymagania ustalono tę różnicę. Dopisanie kolejnych testów ma wtedy sprawdzalny cel.

42 AI
100a⁝ Java