Wie es entscheidet

TestDetta grenzt die Auswahl Schritt für Schritt ein, und nur dort, wo es Belege dafür hat. Alles, was sich nicht nachverfolgen lässt, führt dazu, dass mehr ausgeführt wird.

Mehr auswählen, nie weniger

Ein übersehener Test ist ein Fehler; ein zusätzlicher Test kostet nur CI-Zeit. Jede der folgenden Regeln ergibt sich daraus. Ist sich die Analyse nicht sicher, ob eine Änderung ein Projekt oder eine Klasse betrifft, behandelt sie es als betroffen, und der Grund erscheint in der Ausgabe.

Die fünf Schritte

1. Projekte

Geänderte Dateien werden ihren Projekten zugeordnet und über ProjectReferences zu jedem Projekt verfolgt, das von ihnen abhängt. Build-weite Dateien wie Directory.Build.props, global.json und .editorconfig betreffen jedes Projekt unterhalb von ihnen.

2. Symbole

Bei C#-Dateien werden der alte und der neue Syntaxbaum verglichen. Änderungen an Kommentaren und Leerraum wählen nichts aus; andernfalls weiß TestDetta, welche Typen und Member sich geändert haben.

3. Coverage

Nach td record liefert eine am Basis-Commit aufgezeichnete Map, welche Testklasse welche Methode ausgeführt hat, direkt die Testklassen für geänderte Methoden, Eigenschaften und Konstruktoren.

4. Namen

Was die Coverage nicht sieht, etwa neuer Code, Felder und Konstanten, wird anhand des Namens durch den Quellcode verfolgt, bis es Testklassen oder Code erreicht, den die Coverage-Map kennt.

5. Fallbacks

Ein ganzes Testprojekt läuft, wenn sich eine Änderung nicht nachverfolgen lässt:

Die Selbstprüfung des Filters

TestDetta führt ein ausgewähltes Projekt mit einem Filter auf seine Testklassen aus. Ein Filter, der keinem Test entspricht, wird als Fehler in TestDetta behandelt: Das Projekt wird ohne ihn erneut ausgeführt.

Coverage-Maps

td record führt alle Tests mit Coverage aus und schreibt eine Map pro Testprojekt. Maps werden am besten an der Merge-Base Ihres Branches aufgezeichnet, und in der CI zeichnen Sie für jeden Commit auf dem Standard-Branch eine auf.

Ältere Maps und die Drift-Bridge

Eine Map, die bis zu 50 Commits vor der Merge-Base aufgezeichnet wurde, wird weiterhin verwendet (--max-map-age, Standard 50; 0 verwendet nur eine an der Merge-Base aufgezeichnete Map). Code, der sich dazwischen geändert hat, könnte Aufrufe erzeugt haben, die die Map nicht kennt. Deshalb wählt TestDetta zusätzlich die Klassen aus, die einen seitdem geänderten Member ausgeführt haben, der Ihre Änderung erreichen kann, und verfolgt neuen Code anhand des Namens.

Wenn sich etwas dazwischen nicht durch Member beschreiben lässt, etwa eine Build-Datei oder appsettings.json, laufen die betroffenen Testprojekte vollständig. --drift-fallback name entscheidet sie stattdessen anhand von Namen, wie ohne Map: schneller, aber blind für Aufrufe, die über Konfiguration oder Reflection verbunden sind.

Ohne Map

Die ersten Pull Requests nach der Einrichtung oder solche, die weit zurückliegen, finden keine Map in Reichweite. TestDetta wählt dann nur anhand von Namen aus, was sicher ist, aber mehr auswählt, und vermerkt das in seinem Log. Die Auswahl wird präzise, sobald td record auf dem Standard-Branch gelaufen ist.

Das Warum sehen

td affected und td test --dry-run geben jedes betroffene Testprojekt mit dem Grund aus, warum es gefiltert, vollständig oder gar nicht läuft; td affected --format json ergänzt die Entscheidung für jede Testklasse. -v schreibt jede Entscheidung nach stderr, zum Beispiel warum ein Projekt betroffen war oder welche Änderung eine Klasse ausgelöst hat.

$ td test --dry-run --base origin/main
td: 1 changed file(s), 2 affected test project(s).
Tests to run:
  filtered tests/Shop.Tests/Shop.Tests.csproj  (3 of 41 test class(es) run or reference the changed code)
  skip     tests/Api.Tests/Api.Tests.csproj  (no test class runs or references the changed code)