Wie führe ich einen Gradle-Test durch, wenn alle Tests auf dem neuesten Stand sind?

130

Ich habe mein Notenskript eingerichtet. Wenn ich den Gradle-Build ausführe, funktioniert alles und es werden die jUnit-Tests ausgeführt.

Wenn ich danach den Gradle-Test durchführe, erhalte ich Folgendes:

C:\Users\..\..\Project>gradle test
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE

Wenn ich gradle cleanspiele, funktioniert Gradle Build natürlich ... Ich möchte nur die Tests zurücksetzen können, nicht das gesamte Projekt: Wie soll ich das machen?

USer22999299
quelle
3
Dies erscheint aufgrund der gegebenen Informationen unnötig. Wenn sich weder der Anwendungscode noch der Testcode geändert haben, warum müssen Sie die Tests erneut ausführen?
Jolta
10
@Jolta Einige der Tests in meinem Code beziehen sich auf Eingaben von Drittanbietern. Ich führe meine Tests nicht nur aus, um sicherzustellen, dass ich keinen Fehler in den Code eingefügt habe, sondern auch, um zu überprüfen, ob sich an den Eingaben von Drittanbietern etwas geändert hat das bekomme ich
USer22999299
4
Es tut mir leid, dass ich nicht so wählerisch bin, aber ich denke nicht, dass dies die richtige Art ist, darüber nachzudenken: Wenn Sie variable 3-Parteien-Eingaben haben, ist dies nicht die richtige Art, damit umzugehen, um diese Eingaben auf irgendeine Weise zu verspotten? Beim Testen sollte es eigentlich darum gehen, den Code zu testen, den Sie schreiben. Sind Sie nicht in der offensichtlichen Gefahr, falsch positive Ergebnisse zu erzielen, wenn Sie sich darauf verlassen, dass Eingaben von Drittanbietern inakzeptabel sind? Sollte die Strategie nicht darin bestehen, Problemeingaben als Teil Ihres App-Codes zu berücksichtigen?
Mike Nagetier
9
@mikerodent erwägen, Ihren Code gegen einen Onlinedienst eines Drittanbieters zu testen. Sie möchten mögliche Änderungen in der Service-API überwachen, um so schnell wie möglich mit bereitgestellten Fixes reagieren zu können. Sind CI-Tests nicht ein guter Weg, dies zu tun? Wenn Sie ein Modell verwenden, erfahren Sie nur, dass Ihr eigener Code keine Regressionen aufweist, die Abhängigkeiten jedoch möglicherweise noch Änderungen aufweisen. Die Verwendung des realen Dienstes zeigt an, dass Ihr Produkt die erwarteten Vorgänge in der aktuellen Umgebung tatsächlich ausführen kann.
Elist
5
Dies gilt auch unter dem Gesichtspunkt des Integrationstests, bei dem der Zweck des Tests darin besteht, die Integration Ihres Codes mit anderen Codebits zu validieren, wobei es nicht angebracht wäre, sich über Abhängigkeiten lustig zu machen
1800 INFORMATION

Antworten:

171

Eine Option wäre die Verwendung des --rerun-tasksFlags in der Befehlszeile . Dies würde alle Testaufgaben und alle Aufgaben, von denen es abhängt, erneut ausführen.

Wenn Sie nur daran interessiert sind, die Tests erneut auszuführen, können Sie die Testergebnisse vor dem Ausführen der Tests schrittweise bereinigen. Dies kann mit der cleanTestAufgabe erfolgen.

Hintergrundinformationen - Das Java-Plugin definiert für jede der anderen Aufgaben eine saubere Aufgabe. Laut Dokumentation :

cleanTaskName - Löscht Dateien, die von der angegebenen Aufgabe erstellt wurden. cleanJar löscht die von der JAR-Task erstellte JAR-Datei und cleanTest löscht die von der Testaufgabe erstellten Testergebnisse.

Um Ihre Tests erneut auszuführen, müssen Sie daher nur die cleanTestAufgabe ausführen , dh:
gradle cleanTest test

Amnon Shochot
quelle
3
gradle cleanTest testführt die Tests nicht erneut aus, bereinigt ihre Ausgabe, aber die testAufgabe erhält weiterhin die Testergebnisse aus dem Cache - siehe github.com/gradle/gradle/issues/9153
dan.m war user2321368
3
Der obige Kommentar ist richtig. Aber wenn Sie verwenden --no-build-cache, funktioniert es wie erwartet, z gradle cleanTest test --no-build-cache.
vRallev
51

Eine andere Möglichkeit wäre, Folgendes in Ihr build.gradle aufzunehmen:

test.outputs.upToDateWhen {false}
František Hartman
quelle
1
Ich habe diese Technik für eine funcTestAufgabe verwendet, die ich zum Ausführen von Funktionstests erstellt habe.
Pharsicle
4
Dies ist ein viel besserer Ansatz als die akzeptierte Antwort, da er nur auf die gewünschte Aufgabe angewendet wird. Das upToDateWhenkann auf jede "
codegesteuerte
1
Wie die Antwort stackoverflow.com/a/52484259/340175 erwähnt, gibt es einen nützlichen Blog-Beitrag blog.gradle.org/stop-rerunning-tests, der erklärt, warum dieser Ansatz nicht als allgemeiner Ansatz empfohlen wird. Ich stimme jedoch zu, dass es nützlich sein kann und das erreicht, was die Frage stellt.
JulianHarty
Ja, dies ist eine veraltete Antwort, als ich schrieb, dass Gradle in Version 2.11 war und gerade erst verwendet werden konnte, aber immer noch viele raue Kanten hatte, die heute poliert sind.
František Hartman
1
Gute Antwort!!! Übergeben mit einem Parameter : gradle test -Prerun-tests. Code in build.gradle:if(project.hasProperty("rerun-tests")) { test.outputs.upToDateWhen {false} }
AlikElzin-kilaka
17

Dies war kürzlich das Thema in Gradles Blog-Post. Hören Sie auf, Ihre Tests erneut auszuführen . Der Autor zeigt ein Beispiel mit outputs.upToDateWhen { false }und erklärt, warum es falsch ist:

Dies erzwingt keine Wiederholungen

Was der Autor dieses Snippets wahrscheinlich sagen wollte, ist "Meine Tests immer wiederholen". Das ist jedoch nicht das, was dieser Ausschnitt tut. Die Aufgabe wird nur als veraltet markiert, sodass Gradle gezwungen ist , die Ausgabe neu zu erstellen . Wenn der Build-Cache aktiviert ist, muss Gradle die Aufgabe nicht ausführen, um die Ausgabe neu zu erstellen. Es findet einen Eintrag im Cache und entpackt das Ergebnis in das Ausgabeverzeichnis des Tests.

Gleiches gilt für dieses Snippet:

test.dependsOn cleanTest

Gradle entpackt die Testergebnisse aus dem Build-Cache, nachdem die Ausgabe bereinigt wurde, sodass nichts erneut ausgeführt wird. Kurz gesagt, diese Schnipsel erzeugen ein sehr teures No-Op.

Wenn Sie jetzt denken "Okay, ich werde auch den Cache deaktivieren", lassen Sie mich Ihnen sagen, warum Sie nicht sollten.

Anschließend erklärt der Autor, warum das Wiederholen einiger Tests Zeitverschwendung ist:

Die überwiegende Mehrheit Ihrer Tests sollte deterministisch sein, dh bei gleichen Eingaben sollten sie das gleiche Ergebnis liefern.

In den wenigen Fällen, in denen Sie Tests erneut ausführen möchten, bei denen sich der Code nicht geändert hat, sollten Sie sie als Eingabe modellieren. Hier sind beide Beispiele aus dem Blog-Beitrag, die das Hinzufügen einer Eingabe zeigen, damit die Aufgabe diese bei ihren aktuellen Überprüfungen verwendet.

task randomizedTest(type: Test) {
  systemProperty "random.testing.seed", new Random().nextInt()
}

task systemIntegrationTest(type: Test) {
  inputs.property "integration.date", LocalDate.now()
}

Ich empfehle den gesamten Blog-Beitrag zu lesen.

mkobit
quelle
8
Das klingt gut für den speziellen Anwendungsfall, über den Sie sprechen, aber ich schreibe Tests nach der Bereitstellung für einen externen Live-Webdienst und verwende zufällig Junit und Gradle, um dies zu erreichen. Der zu testende Code befindet sich nicht im Repo, und tatsächlich gibt es keinen "Anwendungscode", da ich tatsächlich ein Live-Produktionssystem anstelle des Codes selbst teste. Danke für die Antwort, das ist sehr nützlich! Ich wollte nur darauf hinweisen, dass es zusätzliche Anwendungsfälle gibt, bei denen die Tests jedes Mal erneut ausgeführt werden müssen, auch wenn sich keiner der Code-Gradle-Änderungen ändert
Brandon
11

Hier ist eine Lösung mit der Datei "build.gradle", falls Sie Ihre Befehlszeile nicht ändern möchten:

test {
    dependsOn 'cleanTest'
    //Your previous task details (if any)
}

Und hier ist die Ausgabe. Beachten Sie 2 Änderungen gegenüber Ihrer vorherigen Ausgabe:

1) In der Ausgabe wird eine neue 'cleanTest'-Aufgabe angezeigt.

2) 'test' wird immer bereinigt (dh niemals 'UP-TO-DATE'), so dass es jedes Mal ausgeführt wird:

$ gradle build
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:findMainClass
:jar
:bootRepackage
:assemble
:cleanTest
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test
:check
:build
TealSeed
quelle
1
Wenn Sie cleanTestzuvor testausgeführt werden, werden die Tests nicht erneut ausgeführt, sondern die Ausgabe wird bereinigt. Die
Testaufgabe ruft
8

--rerun-tasks funktioniert, ist aber ineffizient, da alle Aufgaben erneut ausgeführt werden.

cleanTest allein kann aufgrund des Build-Caches nicht ausreichen.

Der beste Weg, dies zu erreichen, ist:

./gradlew --no-build-cache cleanTest test
masc3d
quelle
0

Außerdem ist das Hinzufügen --rerun-taskswirklich überflüssig. Das passiert nie. Erstellen Sie eine --no-rerun-tasksund machen Sie --rerun-tasksStandard, wenncleanTask

user1648995
quelle
-4

Ich denke, dies ist eine berechtigte Frage, da es in Gradle möglich ist, diesen Befehl auszuführen test, und was passiert, ist, dass nichts passiert!

Aber ich würde die Notwendigkeit in Frage stellen, dies jemals zu tun, wie Jolta in seinem Kommentar sagte: Wenn sich kein Code geändert hat, warum müssen Sie erneut testen? Wenn Sie Zweifel an Eingaben von Drittanbietern haben, würde ich sagen, dass Sie dies in Ihrem App-Code berücksichtigen müssen. Wenn Sie befürchten, dass Ihr Code "schuppig" sein könnte, dh alle Tests beim ersten Mal, aber nicht beim zweiten (oder 100.) Mal bestehen kann, müssen Sie nicht darüber nachdenken, warum Sie diese Zweifel haben, und sie ansprechen?

Persönlich denke ich, dass dies ein (sehr geringfügiger) Designfehler in Gradle ist: Wenn alles vollständig auf dem neuesten Stand ist, anstatt "ERFOLGREICH BAUEN" zu gehen, sollte "KEINE ÄNDERUNG SEIT LETZT ERFOLGREICHES BAUEN: NICHTS GEMACHT" lauten.

Mike Nagetier
quelle
3
"Müssen Sie nicht darüber nachdenken, warum Sie diese Zweifel haben, und sie ansprechen?": Ja, aber um Daten zum Nachdenken zu bringen, möchte ich die Tests einige Male ausführen und sehen, was passiert. Ist das so verrückt
Mhsmith
1
@mikerodent Ich stimme Ihrem Punkt teilweise zu. Es gibt "einfache" Fälle, normalerweise einfache Whitebox-Unit-Tests, bei denen keine Codeänderung nichts wirklich bedeutet, was erneut getestet werden muss. Denken Sie jedoch an Tests mit Abhängigkeiten. "Oh ja, Docker lief nicht usw." Es gibt Tests, bei denen es die Infrastruktur (und in dev you) ist, die Abhängigkeiten einrichtet (sie werden "bereitgestellt") und nicht der Build. In diesen Fällen möchte ich immer wieder ausgeführt werden können.
Dbalakirev
@dbalakirev Ja, das ist mir eingefallen ... aber sollten Sie nicht in der Lage sein, die Rolle dieser Abhängigkeiten wie Docker zu verspotten ...? Ich meine, wenn Sie das nicht tun, speichern Sie dann nicht zukünftige Probleme? Ich sage nicht, dass ich 100% sicher bin, aber ich denke, ich sage, dass Ihre Tests in einer Welt, die zweifellos idealer ist als unsere, alle Grundlagen abdecken sollten.
Mike Nagetier
Sie können sich über yes lustig machen, mit dem Sie eine Abhängigkeit (Docker) haben. Wenn dies bei Ihnen fehlschlägt, bedeutet dies, dass Sie erneut ausgeführt werden möchten, auch wenn sich der Code nicht geändert hat. Ich möchte betonen, dass dieser Gedanke nicht für Unit-Tests oder Tests gilt, bei denen 1. Sie versuchen, Abhängigkeiten zu vermeiden 2. oder sie zumindest mithilfe des Test-Frameworks verspotten, aber wenn sie wirklich "bereitgestellt" werden, wenn Sie so wollen.
Dbalakirev
2
__ Wenn sich kein Code geändert hat, warum müssen Sie erneut testen? __ Haben Sie von Integrationstests gehört?
Bogdan Mart