Freigeben über


Eingabegenerierung mit dynamischer symbolischer Ausführung

IntelliTest generiert Eingaben für parametrisierte Komponententests , indem die Verzweigungsbedingungen im Programm analysiert werden. Testeingaben werden basierend darauf ausgewählt, ob sie neue Verzweigungsverhalten des Programms auslösen können. Die Analyse ist ein inkrementeller Prozess. Es verfeinert ein Prädikat q: I -> {true, false} über die formalen Testeingabeparameter I. q stellt den Satz von Verhaltensweisen dar, die IntelliTest bereits beobachtet hat. Zunächst q := false, da noch nichts beobachtet wurde.

Die Schritte der Schleife sind:

  1. IntelliTest bestimmt Eingaben i so, dass q(i)=false ein Einschränkungslöser verwendet wird. Bei der Konstruktion nimmt die Eingabe i einen Ausführungspfad ein, der noch nicht gesehen wurde. Zunächst bedeutet dies, dass i jede beliebige Eingabe sein kann, da noch kein Ausführungspfad entdeckt wurde.

  2. IntelliTest führt den Test mit der ausgewählten Eingabe iaus und überwacht die Ausführung des Tests und des programms unter Test.

  3. Während der Ausführung nimmt das Programm einen bestimmten Pfad ein, der von allen bedingten Verzweigungen des Programms bestimmt wird. Der Satz aller Bedingungen, die die Ausführung bestimmen, wird als Pfadbedingung bezeichnet, die als Prädikat p: I -> {true, false} für die formalen Eingabeparameter geschrieben wird. IntelliTest berechnet eine Darstellung dieses Prädikats.

  4. IntelliTest konfiguriert q := (q or p). Mit anderen Worten, es zeichnet die Tatsache auf, dass es den Pfad, der durch p dargestellt wird, gesehen hat.

  5. Wechseln Sie zu Schritt 1.

IntelliTests Einschränkungslöser kann werte aller Typen behandeln, die in .NET-Programmen angezeigt werden können:

IntelliTest filtert Eingaben aus, die gegen die angegebenen Annahmen verstoßen.

Neben den unmittelbaren Eingaben (Argumente für parametrisierte Komponententests) kann ein Test weitere Eingabewerte aus der statischen PexChoose-Klasse zeichnen. Die Auswahlmöglichkeiten bestimmen auch das Verhalten parametrisierter Mocks.

Constraint-Lösungsalgorithmus

IntelliTest verwendet einen Einschränkungslöser, um die relevanten Eingabewerte eines Tests und des zu test befindlichen Programms zu ermitteln.

IntelliTest verwendet den Z3-Einschränkungslöser.

Dynamische Codeabdeckung

Als Nebeneffekt der Laufzeitüberwachung sammelt IntelliTest dynamische Codeabdeckungsdaten. Dies wird als dynamisch bezeichnet, da IntelliTest nur über Code weiß, der ausgeführt wurde, daher können keine absoluten Werte für die Abdeckung auf die gleiche Weise wie andere Abdeckungstools geben.

Wenn IntelliTest beispielsweise die dynamische Abdeckung als 5/10-Basisblöcke meldet, bedeutet dies, dass fünf Blöcke von zehn abgedeckt wurden, wobei die Gesamtanzahl der Blöcke in allen Methoden, die bisher durch die Analyse erreicht wurden (im Gegensatz zu allen Methoden, die in der Testassembly vorhanden sind) zehn beträgt. Später in der Analyse, da mehr erreichbare Methoden ermittelt werden, kann sowohl der Zähler (5 in diesem Beispiel) als auch der Nenner (10) erhöht werden.

Ganze Zahlen und Floats

Der Einschränkungslöser von IntelliTest bestimmt die Testeingabewerte von Grundtypen wie Byte, Int, Float und anderen, um unterschiedliche Ausführungspfade für den Test und das zu testende Programm auszulösen.

Objekte

IntelliTest kann entweder Instanzen vorhandener .NET-Klassen erstellen, oder Sie können IntelliTest verwenden, um automatisch Simulierte Objekte zu erstellen , die eine bestimmte Schnittstelle implementieren und sich je nach Verwendung auf unterschiedliche Weise verhalten.

Instanziieren vorhandener Klassen

Was ist das Problem?

IntelliTest überwacht die ausgeführten Anweisungen bei der Ausführung eines Tests und des zu testenden Programms. Insbesondere überwacht sie den gesamten Zugriff auf Felder. Anschließend wird ein Einschränkungslöser verwendet, um neue Testeingaben zu ermitteln, einschließlich Objekte und deren Feldwerte, sodass sich der Test und das programm unter Test auf andere interessante Weise verhalten.

Dies bedeutet, dass IntelliTest Objekte bestimmter Typen erstellen und deren Feldwerte festlegen muss. Wenn die Klasse sichtbar ist und über einen sichtbaren Standardkonstruktor verfügt, kann IntelliTest eine Instanz der Klasse erstellen. Wenn alle Felder der Klasse sichtbar sind, kann IntelliTest die Felder automatisch festlegen.

Wenn der Typ nicht sichtbar ist oder die Felder nicht sichtbar sind, benötigt IntelliTest Hilfe beim Erstellen von Objekten und bringt sie in interessante Zustände, um eine maximale Codeabdeckung zu erzielen. IntelliTest könnte Spiegelung verwenden, um Instanzen auf beliebige Weise zu erstellen und zu initialisieren, aber dies ist in der Regel nicht wünschenswert, da es das Objekt in einen Zustand bringen kann, der während der normalen Programmausführung niemals auftreten kann. Stattdessen basiert IntelliTest auf Hinweise des Benutzers.

Sichtbarkeit

.NET verfügt über ein aufwändiges Sichtbarkeitsmodell: Typen, Methoden, Felder und andere Member können privat, öffentlich, intern und mehr sein.

Wenn IntelliTest Tests generiert, wird versucht, nur Aktionen (z. B. Aufrufen von Konstruktoren, Methoden und Einstellungsfeldern) auszuführen, die in Bezug auf .NET-Sichtbarkeitsregeln im Kontext der generierten Tests zulässig sind.

Die Regeln sind wie folgt:

  • Sichtbarkeit interner Mitglieder

    • IntelliTest geht davon aus, dass die von ihm generierten Tests Zugriff auf interne Member haben, die für die eingeschlossene PexClass sichtbar waren. .NET verfügt über das InternalsVisibleToAttribute, um die Sichtbarkeit interner Member auf andere Assemblys zu erweitern.
  • Sichtbarkeit privater und familiärer (in C#geschützter) Mitglieder der PexClass

    • IntelliTest platziert die generierten Tests immer direkt in der PexClass oder in einer Unterklasse. Daher geht IntelliTest davon aus, dass alle sichtbaren Familienmitglieder (in C# protected) verwendet werden können.
    • Wenn die generierten Tests direkt in die PexClass (in der Regel mithilfe von Partiellen Klassen) platziert werden, geht IntelliTest davon aus, dass sie auch alle privaten Member der PexClass verwenden kann.
  • Sichtbarkeit öffentlicher Mitglieder

    • IntelliTest geht davon aus, dass alle exportierten Member verwendet werden können, die im Kontext der PexClass sichtbar sind.

Parametrisierte Mock-Objekte

Wie teste ich eine Methode mit einem Parameter eines Schnittstellentyps? Oder von einer nicht versiegelten Klasse? IntelliTest weiß nicht, welche Implementierungen später verwendet werden, wenn diese Methode aufgerufen wird. Und vielleicht gibt es nicht einmal eine echte Implementierung zur Testzeit.

Die herkömmliche Antwort besteht darin, simulierte Objekte mit expliziten Verhaltensweisen zu verwenden.

Ein Pseudoobjekt implementiert eine Schnittstelle (oder erweitert eine nicht versiegelte Klasse). Es stellt keine echte Implementierung dar, sondern nur eine Verknüpfung, die die Ausführung von Tests mithilfe des Mock-Objekts ermöglicht. Das Verhalten wird manuell als Teil jedes Testfalls definiert, in dem es verwendet wird. Es gibt viele Tools, mit denen simulierte Objekte und ihr erwartetes Verhalten leicht definiert werden können. Dieses Verhalten muss jedoch weiterhin manuell definiert werden.

Anstelle hartcodierter Werte in Simuliertobjekten kann IntelliTest die Werte generieren. Ebenso wie es parametrisierte Komponententests ermöglicht, ermöglicht IntelliTest auch parametrisierte Modelle.

Parametrisierte Mocks verfügen über zwei verschiedene Ausführungsmodi.

  • auswählen: Beim Untersuchen von Code sind parametrisierte Modelle eine Quelle zusätzlicher Testeingaben, und IntelliTest versucht, interessante Werte auszuwählen.
  • Wiedergabe: Bei der Ausführung eines zuvor generierten Tests verhalten sich parametrisierte Modelle wie Stubs mit Verhalten (d. h. vordefiniertes Verhalten).

Verwenden Sie PexChoose , um Werte für parametrisierte Modelle abzurufen.

Strukturen

Die Begründung von IntelliTest zu Strukturwerten ähnelt der Art und Weise, wie sie mit Objekten umgeht.

Arrays und Zeichenfolgen

IntelliTest überwacht die ausgeführten Anweisungen während der Ausführung eines Tests und des programms unter Test. Insbesondere wird beobachtet, wann das Programm von der Länge einer Zeichenfolge oder eines Arrays abhängt (und die unteren Grenzen und Längen eines mehrdimensionalen Arrays). Außerdem wird beobachtet, wie das Programm die verschiedenen Elemente einer Zeichenfolge oder eines Arrays verwendet. Anschließend wird ein Constraint-Solver verwendet, um zu bestimmen, welche Längen und Elementwerte den Test und das getestete Programm auf interessante Weise beeinflussen könnten.

IntelliTest versucht, die Größe der Arrays und Zeichenfolgen zu minimieren, die erforderlich sind, um interessante Programmverhalten auszulösen.

Abrufen zusätzlicher Eingaben

Die statische PexChoose-Klasse kann verwendet werden, um zusätzliche Eingaben für einen Test zu erhalten und kann zum Implementieren parametrisierter Modelle verwendet werden.

Haben Sie Feedback?

Veröffentlichen Sie Ihre Ideen und Featureanfragen in der Entwicklercommunity.