Metas Tool zur Verbesserung automatisierter Unit-Tests mit großen Sprachmodellen: TestGen-LLM
(arxiv.org)Metas Tool zur Verbesserung automatisierter Unit-Tests: TestGen-LLM
- Das von Meta entwickelte Tool TestGen-LLM nutzt große Sprachmodelle (Large Language Models, LLMs), um bestehende, von Menschen geschriebene Tests automatisch zu verbessern.
- Die von TestGen-LLM generierten Testklassen bestehen erfolgreich eine Reihe von Filtern, die im Vergleich zur ursprünglichen Testsuite eine messbare Verbesserung garantieren und das LLM-Halluzinationsproblem vermeiden.
- Der Einsatz von TestGen-LLM in den Test-a-thons für die Instagram- und Facebook-Plattformen von Meta wird erläutert.
Leistungsbewertung von TestGen-LLM
- In der Bewertung der Instagram-Produkte Reels und Stories wurden 75% der Testfälle von TestGen-LLM korrekt gebaut, 57% bestanden zuverlässig und 25% erhöhten die Abdeckung.
- In den Test-a-thons von Meta für Instagram und Facebook verbesserte TestGen-LLM 11,5% aller angewandten Klassen, und Meta-Softwareingenieure akzeptierten 73% der Empfehlungen für die Bereitstellung.
- Dies ist der erste Bericht über den großskaligen Einsatz von von LLM-generiertem Code und die damit verbundene Zusicherung, dass die Codequalität verbessert wird.
GN⁺-Meinung
- TestGen-LLM ist ein Tool, das im Bereich der Softwaretestautomatisierung und -qualitätssteigerung eine Revolution einleiten könnte, da es bestehende Tests erfolgreich mit großen Sprachmodellen verbessert.
- Es trägt in realen Produktionsumgebungen zur Erhöhung der Testabdeckung bei und generiert dabei zuverlässige Testfälle, womit es einen wichtigen Beitrag für die Software Engineering-Community leistet.
- Der erfolgreiche Einsatz von TestGen-LLM in den Test-a-thons von Meta zeigt, dass eine Integration in die tatsächliche Produktentwicklung möglich ist und dies eine wichtige Entwicklung zur Verbesserung von Effizienz und Stabilität in der Softwareentwicklung darstellt.
1 Kommentare
Meinungen auf Hacker News
Bei einem großen Versicherer, bei dem ich früher gearbeitet habe, setzte das Management für die gesamte Codebasis 80 % Test Coverage als Ziel. Daraufhin begannen Leute, nutzlose Unit-Tests für Getter/Setter von Java-DTOs zu schreiben, nur um das Ziel zu erreichen.
Natürlich konnten die Entwickler auch Sonars Regeln zur Coverage-Messung nicht ändern, und ich lernte schon als junger Entwickler, dass KPIs allein Verhalten fördern können, das der ursprünglichen Absicht widerspricht.
Ein paar gut entworfene E2E-Testszenarien wären für die Softwarequalität vermutlich besser gewesen.
Das Problem war, dass der unordentliche bestehende Code mit 95 % Coverage sehr gut getestet war. Der neue Code hatte 100 % Coverage, war aber viel kürzer, wodurch die Gesamt-Coverage sogar sank und der Check nicht bestand.
Übrig blieb nur Swing-UI-Code, der schwer zu testen war und bei dem die Tests wenig Aussagekraft hatten. Statt also ein bis zwei Wochen Swing-Tests zu schreiben, ließ der Tech Lead den alten Code irgendwo im Repository liegen und ließ die Tests einfach auf diesen Code zeigen.
Am Ende blieben Tausende Zeilen toter Code, der in Produktion niemals aufgerufen wurde, im Repository, nur um Sonar zufriedenzustellen.
Das Ergebnis war, dass in Headern Tausende Konstanten wie
static const unsigned ONE = 1;,TWO = 2;,THREE = 3;entstanden.https://en.m.wikipedia.org/wiki/Mutation_testing
Für nahezu jede Sprache gibt es Tools und Frameworks, zum Beispiel stryker-mutator (C#, TypeScript), pitest (Java) und mutatest (Python).
In sechs Jahren Berufserfahrung hatte ich noch nie eine so schlecht geschriebene Anwendung gesehen. Nicht nur der Stil war schlecht, es gab auch viele tatsächlich komplett kaputte Teile, und niemand wusste, was daran falsch war.
Ich hasse Sonar wirklich. Es sollte nur zum Melden von Schwachstellen verwendet werden, nicht dazu, Variablennamen ändern zu lassen oder zu sagen: „Diese Code-Duplizierung muss refaktorisiert werden.“ Wir haben bereits einen Backlog mit Jira-Tickets; bitte schreibt uns nicht auch noch vor, was wann zu tun ist.
Aber Manager lieben solche Machtspielzeug-Tools viel zu sehr.
Das große Problem ist, dass man so etwas verpflichtend macht und dann einen riesigen bürokratischen Prozess durchlaufen muss, um Dummheiten zu vermeiden. Noch letzte Woche habe ich mich mit einem verpflichtenden Codequalitäts-Tool gestritten, das sich beschwerte, dass bei
res.status(200).json()der HSTS-Header fehle.Selbst wenn man ihn manuell setzte oder
app.use(helmet())verwendete, beschwerte es sich weiter; letztlich wirkte es so, als wolle es, dass das gesamte Backend in einer einzigen Datei steht. Dabei wird HSTS am Ingress oder Load Balancer viel eleganter und automatisch gehandhabt.Ich hätte es als False Positive markieren und ein bis zwei Wochen damit verbringen können, einem höheren Manager zu erklären, was HSTS ist, um eine Freigabe zu bekommen. Stattdessen habe ich dem Response-Objekt-Prototyp
res.sendJson(data, status = 200)hinzugefügt. Das ist eindeutig eine dumme Implementierung, aber es machte mir klar, dass schlechte Software in stark bürokratischen Bereichen als Summe genau solcher schlechten Implementierungen entsteht.Wenn man die Passage sieht, dass „75 % der TestGen-LLM-Testfälle erfolgreich gebaut wurden, 57 % stabil bestanden und 25 % die Coverage erhöhten“, wirkt es problematisch, dass LLM-generierte Tests mit hoher Wahrscheinlichkeit fehlerhaftes Verhalten „absegnen“ könnten.
Besonders bei Codebasen, die ohnehin schon eine geringe Test-Coverage haben, dürfte das zutreffen. Wenn Menschen selbst neue Tests schreiben, hat das den Vorteil, dass jemand beurteilen kann, ob das System dumm ist oder der Test falsch ist.
Mindestens sollten solche Tests in einem speziellen Testordner getrennt werden und mit einem angemessenen Maß an Skepsis behandelt werden.
Eine Codebasis mit guter Coverage ermöglicht es allerdings, große Refactorings sicher ohne Regressionen durchzuführen, und das ist eine nützliche Eigenschaft, selbst wenn es Bugs gibt und das Refactoring diese Bugs unverändert erhält.
Die Gefahr eines Testgenerierungs-Tools, das dafür ausgelegt ist, das aktuelle Verhalten zu kodifizieren, liegt darin, dass man in ein falsches Sicherheitsgefühl geraten kann, obwohl es tatsächlich nur das aktuelle Verhalten kodifiziert hat.
Vielleicht ließe sich das lösen, indem man so etwas nicht „Tests“, sondern eher „Verhaltens-Snapshots“ nennt. Der Name sollte ausdrücken, dass nicht korrektes Verhalten, sondern aktuelles Verhalten erfasst wird.
Deshalb braucht es meiner Meinung nach immer ein gewisses Maß an menschlicher Aufsicht, um zu beurteilen, welches Szenario eingetreten ist.
So etwas passiert in allen möglichen Systemen, und Menschen neigen dazu zu glauben, es ließe sich lösen, indem man wie hier einfach eine weitere Automatisierungsschicht darüberlegt. Tests wurden ursprünglich erfunden, um zu prüfen, ob ein Programm korrekt funktioniert; wenn man sogar das automatisiert, begegnet man demselben Problem wieder in größerem Code, nur eben in Form von Tests statt Assertions.
Es gibt Fälle, in denen man zwar nicht weiß, wie man Factories für die für Tests nötigen Nebeneingaben bauen soll, aber sehr wohl weiß, wie sich der Code selbst verhalten sollte.
Wenn ein LLM das Testgerüst anlegt und es Entwicklern leicht macht, die Business-Logic-Prüfungen zu schreiben, kann das ein großer Gewinn sein.
Wenn die generierten Tests allerdings wie die meisten Unit-Tests zu stark an die Implementierung gekoppelt sind, wird das die Entwicklung verlangsamen. Wenn einzelne Tests zu schwer zu reparieren sind, könnte man sogar sehen, dass Leute bei großen Änderungen alle Tests löschen und neu generieren.
Ein Teil des Codes könnte von diesem Bug abhängen, und wenn man ihn versehentlich oder absichtlich behebt, könnten schwerwiegendere Probleme entstehen.
Natürlich können solche Tests keine Tests ersetzen, die tatsächliche Anforderungen prüfen.
Aber es gibt unzählige Legacy-Systeme, die mit niedriger Coverage in den Wartungsmodus gegangen sind, und in solchen Fällen sind generierte Tests, die das aktuelle Verhalten verifizieren, sehr nützlich. Sie ermöglichen zu prüfen, ob nach einer Änderung der Rest unverändert geblieben ist.
Nachdem ich das PDF gelesen habe, scheint es hier „nur“ darum zu gehen, Tests zu erzeugen, die wiederholt bestehen, also nicht flaky sind.
Der Hauptzweck ist, aus Tests, die das Verhalten des bestehenden Codes fixieren, eine Regressionstest-Suite zu erstellen, nicht Entwicklertests zu ersetzen, die mit Kenntnis der funktionalen Anforderungen geschrieben werden.
Bei einem Unternehmen, bei dem ich vor fast 20 Jahren gearbeitet habe, haben wir auch AgitarOne ausprobiert; das Versprechen war, automatisch Testfälle zu generieren, die das Verhalten von Java-Code erkunden. Außerdem konnte man fast automatisch bestandene Tests erzeugen und als Regressionstest-Suite verwenden.
Mir persönlich gefiel das nicht. Es entstand zu viel Zeug, und das Management verstand steigende Coverage so, dass damit auch die Qualität steige. Ich frage mich, wie viel besser der LLM-Ansatz, von dem FB hier spricht, im Vergleich zu damals ist.
http://www.agitar.com/solutions/products/agitarone.html
Bis ein LLM tatsächliche Korrektheit beurteilen kann, ohne sich auf die Annahme, dass gute Tests bestehen, oder auf ein Orakel zu stützen, scheint man kaum darüber hinauszukommen. Im Prompt müsste man in irgendeiner Form Verhaltenserwartungen enthalten.
Der Wert von Tests liegt darin sicherzustellen, dass nichts kaputtgeht, worum sich jemand kümmert, nicht darin, jedes kaum genutzte Edge-Case-Verhalten, das lediglich ein Nebenprodukt einer bestimmten Implementierung ist, für immer festzuschreiben.
Meiner Erfahrung nach ist das Schreiben von Tests normalerweise eine hervorragende Methode, um die Codequalität zu beurteilen.
Wenn Tests kompliziert sind oder es schwierig ist, Coverage zu erreichen, ist das ein starkes Indiz dafür, dass der zu testende Code verbessert werden sollte.
Code mit geringer Kopplung, hoher Kohäsion und niedriger Komplexität sollte sich leicht mit Unit-Tests testen lassen.
In der Produktevaluierung von Reels und Stories bei Instagram sollen 75 % der TestGen-LLM-Testfälle erfolgreich gebaut worden sein, 57 % stabil bestanden haben und 25 % die Coverage erhöht haben.
Bei Testaktionen für Instagram und Facebook von Meta sollen 11,5 % aller betroffenen Klassen verbessert worden sein, und 73 % der Empfehlungen wurden von Meta Software Engineers für die Produktionsbereitstellung freigegeben.
Ich weiß nicht, ob das gute Quoten sind. Man müsste mehr darüber lesen, ob die nicht akzeptablen Dinge Kleinigkeiten waren, die wir im Code-Review abfangen würden, oder gravierende Probleme. Ein menschlicher Engineer mit einer Fehlerquote von 25 % wäre je nach Art der Fehler vielleicht nicht besonders hilfreich.
Ich frage mich auch, ob die gesamte Mission, die Erstellung von Unit-Tests für Android-Code zu automatisieren, überhaupt in die richtige Richtung geht. Die TDD-Leute dürften sich im Grab, oder zu Hause im Bett, herumwälzen. Vermutlich haben sie aber hinten noch einen Vorbehalt angehängt.
Bei unlogged.io lag der Fokus eine Zeit lang auf der automatischen Generierung von JUnit-Tests, aber aus mehreren Gründen hat das nicht richtig gezündet.
Der generierte Testcode war zu umfangreich, sodass Entwickler ihn nicht warten wollten, er konnte keine realen Szenarien simulieren, und Code Coverage war eine Vanity Metric. Entwickler fanden Workarounds, um mit bedeutungslosen Szenarien die Zielwerte zu erreichen.
Inzwischen arbeiten wir daran, No-Code-Replay-Tests bereitzustellen, die alle einzigartigen Produktionsszenarien simulieren und die Entwickler lokal mit gemockten externen Abhängigkeiten wiedergeben können.
Zur Einordnung: Ich bin der Gründer von unlogged.io.
Ich würde gern den umgekehrten Weg gehen. Man gibt Akzeptanzkriterien ein, daraus werden Tests generiert, die sie überprüfen, und anschließend wird Code generiert, der diese Tests besteht.
Mit Copilot kann man das eingeschränkt gelegentlich ähnlich machen, aber ich verstehe nicht, warum sich gefühlt niemand auf diese Reihenfolge konzentriert.
TestGen-LLM ist wirklich ein seltsames Produkt. Als erster Schritt bei einem Refactoring oder Rewrite könnte es brauchbar sein, aber dass das Paper Code Coverage betont, wirkt wie ein völliger Fehlgriff im Urteil.
Wenn eine Organisation bereits dadurch kaputt ist, dass sie hohe Coverage verlangt, kann es vielleicht nützlich sein, aber TestGen-LLM macht den Projektcode in keiner Weise besser und erhöht nur die Reibung bei der Umsetzung echter Verbesserungen.
Deutlich nützlicher wäre es, Edge-Case-Tests zu erzeugen, die bestehen können oder auch nicht; TestGen-LLM verlässt sich stattdessen darauf, LLM-Müll über Compilerfehler und fehlschlagende Tests auszusortieren.
Dass das Paper überhaupt keine Beispiele für generierte Tests enthält, lässt mich vermuten, dass sie genauso amateurhaft sind wie anderer LLM-generierter Code, den ich bisher gesehen habe.
Es half sogar dabei zu verstehen, was der Code eigentlich tun sollte.
Interessant ist, dass Meta-Mitarbeiter ein 12-seitiges Paper geschrieben haben, um AI für Entwickler zu bewerben, und dafür sogar ein Sankey-Diagramm hervorgeholt haben.
Ich kann mich irren, aber wenn man so etwas veröffentlicht, sollte man doch auch reproduzierbare Informationen bereitstellen.
Das ist keine Verschwörungstheorie; ich habe nur nicht die Art von Daten, die Meta fürs Training nutzt. Ich frage mich, ob sie vielleicht irgendetwas veröffentlicht haben.
Ich frage mich, wie hoch künftig die Kosten für die Wartung riesiger automatisch generierter Testkorpora sein werden.
Man muss nicht nur Testfälle generieren, sondern auch eine automatisierte Methode zum Aktualisieren bereitstellen.