Dieser Artikel wurde maschinell übersetzt.
Generierungstest
Automatisierte Komponententests für veralteten Code mit Pex
Nikhil Sachdeva
In meinem früheren Leben konnte als Berater. Einer meiner Kunden, eine führende Bank wollte seine Kredit Ursprung automatisieren. Die Bank musste ein System bereits vorhanden, die einer Windows-basierten Anwendung, einem proprietären Back-End und einem Mainframesystem, die auch das Kernstück Ihrer Lösung war enthalten. Meine Auftrag wurde zum Integrieren vorhandener Systeme mit einer Reihe von Anwendungen, die für die Konten Division entwickelt wird.
Der Client hat einen Webdienst erstellt, die in den Mainframe mitgeteilt. Auf den ersten Blick schien es ziemlich einfach. Alle musste dazu in den Dienst zu verknüpfen, erhalten Sie die Informationen und an die neue Konten Anwendung übergeben wurde. Aber es ist nie so einfach.
Während der Implementierung fand ich neue Systeme ein Kredit Auftrageber Attribut erwartet, dass die GetLoanDetails-Webdienstmethode hat nicht die Informationen zurückgegeben. Wie sich herausstellt, dass der Dienst vor Jahren von einem Entwickler erstellt wurde, der nicht länger mit der Firma ist. Die Bank hat den Dienst ohne Änderungen verwendet, da Sie viele Schichten und jeder fürchten ist, etwas zu beschädigen.
Dieser Webdienst ist Legacycode.
Schließlich integriert wir einen Wrapper lightweight-Dienst, sodass neue Systeme konnte es für alle neuen Informationen aufrufen und den alten Dienst auch weiterhin verwenden. Es hätten viel einfacher, den vorhandenen Dienst zu ändern, wenn ein Entwurfs konsistentes und getestet werden befolgt worden war.
Code Fresh beibehalten
Legacycode ist etwas, das in der Vergangenheit entwickelt wurde und noch verwendet wird, jedoch ist schwierig zu verwalten und zu ändern. Die Gründe für diese Systeme im Allgemeinen aufrechterhalten drehen sich um die Kosten und Zeit, die für das Erstellen eines ähnlichen Systems für die neuen – Obwohl manchmal ein Mangel an Bewusstsein über die Auswirkungen des zukünftigen Bemühungen für aktuelle Codierung vorhanden ist.
Tatsache ist, dass über einen bestimmten Zeitraum Code beginnt auf rot. Dies kann aufgrund von Änderungen der Anforderung, einen nicht-sehr-gut-Gedanken-through-Entwurf, angewendeten Antimuster oder Mangel an entsprechende Tests sein. Das Endergebnis ist Code, der schwer zu verwalten und schwierig zu ändern ist.
Es gibt viele Ansätze, um zu verhindern, dass Ihr Code rotting kann, eine der effizientesten jedoch werden Sie Code schreiben, der getestet werden kann und über ausreichende Komponententests für den Code zu generieren. Komponententests fungieren als Agents, die ständig untersuchen das System für aufgedeckt Pfade, mühsam sein, Fehler erkennen und Bereitstellen von Indikatoren unabhängig davon, ob Änderungen in einem Subsystem eingeführt, ein gut oder schlecht Auswirkungen auf die gesamte Software haben. Komponententests können das Vertrauen der Entwickler, dass der Code Änderungen keine Regressionen eingeführt wird.
Dass sagte, erstellen und verwalten eine gute Einheit Suite testen, kann eine Herausforderung in sich selbst sein. Es ist möglich, landen mehr Code für die Testsuite als der getestete Code schreiben. Eine weitere Herausforderung ist Abhängigkeiten innerhalb des Codes. Je komplexer Klassen die Lösung, die Weitere Abhängigkeiten, die Sie wahrscheinlich zwischen gefunden werden. Mocks und Dummyroutinen sind eine bekannte Möglichkeit, diese Abhängigkeiten zu entfernen und den Code isoliert testen, aber diese zusätzlichen Kenntnisse erfordern und vom Entwickler zum Erstellen von effektiven Komponententests auftreten.
Um die Rescue pex
Pex ( research.microsoft.com/projects/pex/ ) ist ein Tool entwickelt von Microsoft Research, automatisch und systematisch den minimalen Satz von Test Eingaben Zeitbedarf zum Ausführen einer endlichen Anzahl von begrenzten Pfade zu erstellen. Pex erzeugt automatisch eine kleine Testsuite mit hoher Code- und Assertionsabdeckung.
Pex sucht interessante ein-/ Ausgabe Ihre Methoden auf, die dann als eine kleine Testsuite mit hohen Codeabdeckung gespeichert werden kann. Pex führt eine systematische Analyse Stellensuche für Abgrenzungsbedingungen, Ausnahmen und Assertionsfehler, die Sie sofort Debuggen können. Pex ermöglicht außerdem das parametrisierte Komponententests (PUT), eine Erweiterung des Komponententests, Test Wartungskosten reduziert, und es verwendet dynamische symbolische Ausführung können über den Code unter Test So erstellen Sie eine Testsuite verdeckt die meisten Verzweigungen der Ausführung untersuchen.
Ein PUT ist einfach eine Methode, die Parameter annimmt, ruft der Code unter Test und Assertionen besagt. Ein Beispiel sieht PUT wie folgt aus:
void AddItem(List<int> list, int item) {
list.Add(item);
Assert.True(list[list.Count - 1] == item);
}
PUT-Konzept ist abgeleitet von einer breiteren Terminologie von datengesteuerten Testen (DDT) aufgerufen, die verwendet wurde für längere Zeit in herkömmlichen Komponententests um die Tests wiederholbar machen. Da herkömmlichen Komponententests Natur geschlossen sind, ist die einzige Möglichkeit, eingegebenen Werte für diese bereitzustellen über externen Quellen, z. B. eine XML-Datei, Kalkulationstabellen oder einer Datenbank. Während der DDT-Ansatz gut funktioniert, zusätzliche Aufwand verwalten und Ändern der externen Datendatei beteiligt ist, und die Eingaben sind Kenntnisse über das System des Entwicklers abhängig.
Pex beruht nicht auf externen Quellen, und bietet stattdessen Eingaben für die Test-Methode durch Übergeben von Werten an die entsprechenden PUT. Da die PUT eine open-Methode ist, können Sie auf eine beliebige Anzahl von Eingaben annehmen, die angeordnet werden. Pex wird darüber hinaus keine zufälligen Werte für die PUT generiert. Er beruht auf der zu testende Methode Introspection und generiert sinnvolle Werte basierend auf Faktoren wie Abgrenzungsbedingungen, zulässigen Werte und eine Einschränkung der neuesten Solver namens Z3 (research.microsoft.com/en-us/um/redmond/projects/z3/ ). Auf diese Weise wird sichergestellt, dass alle relevanten Pfade der zu testende Methode behandelt werden.
Der Vorteil des Pex ist, dass es aus der PUT herkömmlichen Komponententests generiert. Diese Komponententests können direkt in einer Einheit Testframework wie MSTest in Visual Studio ohne Änderungen ausgeführt werden. Pex stellt Erweiterungen zum Generieren von Komponententests für Frameworks wie NUnit oder xUnit.NET. Sie können auch eigene benutzerdefinierte Erweiterung erstellen. Eine Pex generiert herkömmlichen Unit Test sieht wie folgt aus:
[TestMethod]
[PexGeneratedBy(typeof(TestClass))]
void AddItem01() {
AddItem(new List<int>(), 0);
}
Dynamische symbolische Ausführung ist die Pex-Antwort auf unsystematische Tests. Mit diesem Verfahren führt Pex den Code mehrfach auf um das Verhalten der Anwendung zu verstehen. Den Steuerelement und Datenfluss überwacht und erstellt ein Einschränkungssystem-für die Test-Eingaben.
Komponententests mit Pex
Die erste Schritt besteht darin, ein PUT für den Code unter Test zu erstellen. Der PUT kann manuell vom Entwickler oder durch Verwenden der Visual Studio-add-in für Pex generiert werden. Sie können die PUT anpassen, indem Parameter ändern, Hinzufügen von Pex Factorys und Stubs, Mocks integrieren, Assertionen usw. hinzufügen. Pex-Factorys und Dummyroutinen werden weiter unten in diesem Artikel behandelt.
Derzeit nur in c# das Visual Studio-add-in für Pex erstellt PUTs kann, aber des getestete Codes in jeder .NET-Sprache.
Die zweite Schritt nach der PUT eingerichtet wurde, besteht darin, die Untersuchung Pex auszuführen. Dies ist, in dem Pex seine magische verfügt. Es analysiert die PUT identifizieren, was getestet wird. Danach wird die Überprüfung von der getestete Code beginnt, indem über jede Verzweigung übergeben und die möglichen Eingabewerte ausgewertet. Pex wird wiederholt ausgeführt, den getestete Code. Nach jeder Ausführung wählt er eine Verzweigung, die wurde nicht zuvor behandelt, ein Einschränkungssystem (ein Prädikat über die Test-Eingaben) erstellt, um diesen Zweig zu erreichen und verwendet dann eine Einschränkung Solver, um neue Test-Eingaben bestimmen ggf.. Der Test erneut ausgeführt wird, mit der neuen Eingaben und dieser Vorgang wiederholt.
Bei jeder Ausführung möglicherweise Pex entdecken Sie neuen Code und untersuchen tiefer in die Implementierung. Auf diese Weise wird untersucht, Pex das Verhalten des Codes.
Beim Erforschen des Codes, erzeugt Pex eine Testsuite Einheit, die Tests enthält, die alle Zweige abdecken, die Pex ausüben kann. Diese Tests sind standard- unit-Test, der in Visual Studio-Test-Editor ausgeführt werden kann. Wenn Sie eine niedrigere Abdeckung für einige Abschnitte finden, scheint der Code durch die Umgestaltung und das anschließende Anwenden des gleichen Zyklus erneut erreichen höhere Codeabdeckung und eine umfassendere Testsuite überarbeiten.
Pex kann Reduktion der Aufwand und Zeit beim Umgang mit Legacycode beteiligt. Da Pex automatisch die unterschiedlichen Verzweigungen vorsieht und Codepfade erforscht, müssen Sie nicht alle Details der die gesamte Codebasis verstehen. Ein weiterer Vorteil besteht darin, dass der Entwickler auf der Ebene der PUT funktioniert. Das Schreiben einer PUT ist oft viel einfacheren Schreiben von Komponententests geschlossen, da Sie auf das Problem Szenario statt auf alle möglichen Testfälle für die Funktionalität konzentrieren.
Einfügen von Pex für die Zusammenarbeit
Let’s Pex auf einem Blatt Legacycode verwenden, und erfahren Sie, wie Sie können den Code besser verwaltbar und problemlos testen machen.
Fabrikam, einem führenden Hersteller von Basketballs und Baseballs, bietet ein online-Portal, in dem der Benutzer verfügbaren Produkte anzeigen kann, und platzieren Sie Aufträge für diese Produkte. Der Lagerbestand Details werden in einem benutzerdefinierten Datenspeicher, der Zugriff erfolgt über eine Warehouse-Komponente, die Verbindung zum Datenspeicher und Vorgänge wie HasInventory und Remove bereitstellt. Eine Order-Komponente stellt eine Fill-Methode, die ein Auftrag auf Grundlage des Produkts und die Menge, die vom Benutzer übergebene verarbeitet.
Die Reihenfolge und WareHouse-Komponenten sind eng miteinander gekoppelt. Diese Komponenten vor Jahren entwickelt wurden und keine aktuellen Mitarbeiter sind gründliche Kenntnisse über das System. Keine Komponententests wurden während der Entwicklung erstellt, und wahrscheinlich als ein Ergebnis zu erzielen, die Komponenten sehr instabil sind. Abbildung 1 zeigt das aktuelle Design.
Abbildung 1 A Legacy Order Fulfillment-System
Die Fill-Methode der Order-Klasse sieht folgendermaßen aus:
public class Order {
public bool Fill(Product product, int quantity) {
// Check if WareHouse has any inventory
Warehouse wareHouse = new Warehouse();
if (wareHouse.HasInventory(product, quantity)) {
// Subtract the quantity from the product in the warehouse
wareHouse.Remove(product, quantity);
return true;
}
return false;
}
}
Es gibt einige wichtige Elemente, die hier Aufmerksamkeit erfordern. Erstens sind Order und Warehouse eng gekoppelt. Die Klassen hängen von Implementierungen, die weniger erweiterbar und schwer zu Mock verwenden oder stub Frameworks zu machen. Es stehen keine Komponententests so, dass jede Änderung Regressionen resultieren in einem instabilen System führen möglicherweise zur Verfügung.
Die Warehouse-Komponente wurde vor kurzem geschrieben und das aktuelle Entwicklungsteam hat keine Kenntnisse über die Verwendung zu ändern oder die Auswirkungen von Änderungen. Um Themen komplizierter zu machen, kann nicht mit anderen Implementierungen des Warehouse ohne Änderungen Order arbeiten.
Umgestaltung des Codes und anschließend zum Generieren von Komponententests mit Pex Let’s versuchen. Ich wird gestalten Sie die Reihenfolge und Warehouse-Objekte, und erstellen Sie Komponententests für die Fill-Methode der Order-Klasse.
Umgestaltung Legacycode ist offensichtlich eine Herausforderung. Ein Ansatz in diesen Fällen könnte sein, damit mindestens den Code getestet werden, damit ausreichende Komponententests generiert werden können. Ich wird werden nur die nackte minimalen Muster anwenden, die den Code getestet werden.
Hier das erste Problem ist, dass Order eine bestimmte Implementierung der Warehouse verwendet wird. Dies erschwert die Warehouse-Implementierung von der Reihenfolge zu entkoppeln. Let’s ändern Sie den Code ein wenig erleichtern flexibler und getestet werden.
Erstellen Sie eine Schnittstelle IWareHouse, und ändern das Warehouse-Objekt, das diese Schnittstelle implementieren. Alle neuen Warehouse benötigen Sie für diese Schnittstelle implementiert werden.
Da Order eine direkte Abhängigkeit auf Warehouse verfügt, sind eng gekoppelt. Ich verwende Abhängigkeitsinjektion, um die Klasse für Erweiterung seines Verhaltens zu öffnen. Bei diesem Ansatz wird eine Instanz der IWareHouse auf Reihenfolge zur Laufzeit übergeben. Der neue Entwurf ist in Abbildung 2 dargestellt.
Abbildung 2 überarbeitete System mit IWareHouse
Abbildung 3 zeigt die neue Order-Klasse.
Abbildung 3 überarbeitete Order-Klasse
public class Order {
readonly IWareHouse orderWareHouse;
// Use constructor injection to provide a wareHouse object
public Order(IWareHouse wareHouse) {
this.orderWareHouse = wareHouse;
}
public bool Fill(Product product, int quantity) {
// Check if WareHouse has any inventory
if (this.orderWareHouse.HasInventory(product, quantity)) {
// Update the quantity for the product
this.orderWareHouse.Remove(product, quantity);
return true;
}
return false;
}
}
Erstellen von parametrisierten Komponententests
Let’s jetzt verwenden Pex, um Tests für den umgestalteten Code generieren. Pex enthält ein Visual Studio-add-in, das Generieren von macht leicht versetzt. Klicken Sie mit der rechten Maustaste auf das Projekt, Klasse oder Methode für die der PUTs müssen generiert werden, und klicken Sie auf Pex | Stubs für parametrisierte Unit Test erstellen. Ich habe gestartet, indem Sie die Fill-Methode der Order-Klasse.
Pex können Sie mit einem vorhandenen Testprojekt Einheit oder eine neue zu erstellen. Gibt es auch Ihnen Optionen zum Filtern von Tests, die auf Grundlage Methode oder den Typ namens (siehe Abbildung 4 ).
Abbildung 4 Einrichten eines neuen Projekts Pex
Pex generiert die folgenden PUT für die Fill-Methode.
[PexClass(typeof(Order))]
[TestClass]
public partial class OrderTest {
[PexMethod]
public bool Fill([PexAssumeUnderTest] Order target,
Product product, int quantity) {
// Create product factory for Product
bool result = target.Fill(product, quantity);
return result;
}
}
Die OrderTest ist nicht einfach eine normale TestClass; es hat versehen wurde, mit einem PexClass-Attribut ab, der angibt, dass es mit Pex erstellt wurde. Es gibt derzeit keine TestMethod wie in einem standardmäßigen Visual Studio-Komponententest erwartet. Stattdessen müssen Sie eine PexMethod. Diese Methode ist eine parametrisierte Komponententest. Später, wenn Sie die Pex den getestete Code untersuchen lassen, wird eine andere partielle Klasse erstellt, die die Standardeinheit Tests mit den Attributen TestMethod-Attribut versehen enthält. Diese generierten Tests kann über den Visual Studio-Test-Editor zugegriffen werden.
Beachten Sie, dass die PUT für die Fill-Methode drei Parameter annimmt.
[PexAssumeUnderTest] Reihenfolge Ziel
Dies ist die Klasse unter Test selbst. Das PexAssumeUnderTest-Attribut weist Pex, dass nur Werte ungleich Null des angegebenen genauen Typs übergeben sollten.
Produkt Produkt
Dies ist die Product-Basisklasse. Pex wird versuchen, um Instanzen der Klasse Produkt automatisch zu erstellen. Für eine präzisere Steuerung können Sie mit den Factorymethoden Pex bereitstellen. Pex wird diese Factorys verwenden, um Instanzen von Klassen, die komplexe zu erstellen.
Int Menge
Pex wird für die Menge, abhängig von der Methode testenden Werte bereitstellen. Es wird versucht, Einfügen von Junk-e-Mail-Werte, sondern Werte, die für den Test von Bedeutung sind.
Pex Factories
Wie bereits erwähnt, verwendet Pex eine Einschränkung Solver, um neue Test Eingaben für Parameter zu bestimmen. Die Eingaben möglich .NET Standardtypen oder benutzerdefinierte Geschäftsentitäten. Während der Untersuchung erstellt Pex tatsächlich Instanzen dieser Typen, so dass die Anwendung die zu testende interessante unterschiedlich Verhalten kann. Wenn die Klasse angezeigt wird und verfügt über einen Standardkonstruktor sichtbar, kann Pex eine Instanz der Klasse erstellen. Wenn alle Felder angezeigt werden, können Sie Werte für diese auch generieren. Wenn die Felder mit Eigenschaften eingekapselt sind oder nicht nach außen verfügbar gemacht, erfordert Pex Hilfe zum Erstellen des Objekts, um eine bessere Codeabdeckung zu erreichen.
Pex bietet zwei Hooks zu erstellen und verknüpfen Sie mit der Pex-Untersuchung erforderlichen Objekte. Der Benutzer kann Factorys für komplexe Objekte bereitstellen, damit die Pex anderes Objekt Zustände untersuchen kann. Dies wird durch Pex Factory Methode erreicht. Die Typen, die über solche Factorys erstellt werden können, werden explorable Typen genannt. Wir werden diesen Ansatz in diesem Artikel verwenden.
Der anderen Ansatz besteht darin, zu die invarianten der privaten Objekte Felder definieren, damit Pex Zustände anderes Objekt direkt herstellen kann.
Zurückzukommen um Beispielszenario, wenn Sie eine Pex-Untersuchung für die generierten parametrisierte Tests ausführen, wird im Fenster Ergebnisse der Pex Exploration eine Nachricht “ 2-Objekt erstellen. ” anzeigen Hierbei handelt es sich nicht um einen Fehler. Während der Untersuchung Pex ist eine komplexe Klasse (in diesem Fall Order) und eine Standard-Factory für diese Klasse erstellt. Diese Factory wurde von Pex benötigt, um das Programmverhalten besser zu verstehen.
Die Standard-Factory erstellt durch Pex ist eine herkömmlichen Implementierung der Klasse erforderlich. Sie können diese Factory um eigene benutzerdefinierte Implementierung bereitzustellen anpassen. Klicken Sie auf Annehmen/bearbeiten Factory, die den Code in Ihr Projekt eingefügt werden sollen (siehe Abbildung 5 ). Alternativ können Sie eine statische Klasse mit einer statischen Methode erstellen, die mit der PexFactoryMethod-Attribut versehen ist. Beim Erforschen, wird Pex im Testprojekt für alle statischen Klassen mit Methoden mit diesem Attribut suchen und verwenden Sie entsprechend.
Abbildung 5 Standard-Factory erstellen
OrderFactory sieht folgendermaßen aus:
public static partial class OrderFactory {
[PexFactoryMethod(typeof(Order))]
public static Order Create(IWareHouse wareHouseIWareHouse) {
Order order = new Order(wareHouseIWareHouse);
return order;
}
}
Wenn Sie in anderen Assemblys Factorymethoden schreiben, können Sie Pex auf deklarative Weise verwenden, mithilfe der Assemblyebene PexExplorableFromFactoriesFromType oder PexExplorableFromFactoriesFromAssembly-Attribute, z. B. mitteilen.
[assembly: PexExplorableFromFactoriesFromType(
typeof(MyTypeInAnotherAssemblyContainingFactories))]
Wenn Pex nur sehr wenige Tests erstellt wird oder ausfällt, interessante Tests erstellen, indem Sie einfach eine NullReferenceException auslösen, auf das Objekt erstellt werden soll, ist dies ein guter Hinweis darauf, dass Pex eine benutzerdefinierte Factory möglicherweise benötigt werden. Anderenfalls kommt Pex mit einem Satz von Heuristiken Object Factories, die in vielen Fällen erstellen.
Pex-Stubs Framework
In der Softwareentwicklung bezieht sich auf ein dummy-Implementierung, die eine möglicherweise komplexe Komponente zur Erleichterung der Tests zu ersetzen, kann das Konzept der Test-Stub. Obwohl das Konzept der Stubs einfach ist, sind die meisten vorhandenen Frameworks, die helfen können, erstellen und pflegen Sie dummy Implementierungen recht komplex. Das Pex-Team hat eine neue kompakte Framework entwickelt, mit dem Sie einfach Stubs rufen. Stubs generiert Stub-Typen für nicht versiegelte Klassen und Schnittstellen für .NET.
Dieses Framework bietet ein Stub vom Typ T eine Standardimplementierung der einzelnen abstrakte Member von T und einen Mechanismus, um dynamisch eine benutzerdefinierte Implementierung jedes Members angeben. (Optional können Stubs auch für nicht abstrakte virtuelle Member generiert werden.) Die Stub-Typen werden als C#-Code generiert. Das Framework stützt sich ausschließlich auf Delegaten, um das Verhalten der Stub Member dynamisch anzugeben. Stubs unterstützt das .NET Framework 2.0 und höher und mit Visual Studio 2008 und höher integriert.
In meinem Beispielszenario des Auftrags hat Typ eine Abhängigkeit der Warehouse-Objekt. Denken Sie daran, dass ich den Code zum Implementieren von Abhängigkeitsinjektion, so dass Warehouse Zugriff aus bereitgestellt werden kann umgestaltet außen in den Order-Typ. Ist praktisch, wenn die Stubs erstellen.
Erstellen einen Stub ist recht einfach. Sie benötigen lediglich ein .stubx-Datei. Wenn Sie das Testprojekt über Pex erstellt haben, sollten Sie bereits verfügen. Wenn dies nicht der Fall ist, wird diese Datei kann von Visual Studio erstellt werden. Klicken Sie mit der rechten Maustaste auf das Testprojekt, und klicken Sie auf Neues Element hinzufügen. Ein Vorlage-Stubs steht (siehe Abbildung 6 ).
Abbildung 6 Erstellen Sie eine neue Stub
Die Datei wird als standard-XML-Datei in Visual Studio angezeigt. Das Assemblyelement Geben Sie den Namen der Assembly für die die Stubs müssen erstellt werden, und speichern Sie die Datei .stubx:
<Stubs xmlns="https://schemas.microsoft.com/stubs/2008/">
<Assembly Name="FabrikamSports" />
</Stubs>
Pex erstellt automatisch die erforderlichen Stub-Methoden für die Typen in der Assembly.
Die generierte Stub Methoden haben entsprechenden Delegaten Felder, die Hooks für Stubs Implementierungen bereitstellen. Standardmäßig wird Pex eine Implementierung für den Delegaten bereit. Sie können auch einen Lambda-Ausdruck zum Hinzufügen von Verhalten an den Delegaten oder verwenden Sie den PexChoose, Pex, die Werte für die Methode automatisch generieren lassen bereitstellen.
Beispielsweise können Auswahlmöglichkeiten für die HasInventory-Methode bereitzustellen, etwa wie folgt sein:
var wareHouse = new SIWareHouse() {
HasInventoryProductInt32 = (p, q) => {
Assert.IsNotNull(p);
Assert.IsTrue(q > 0);
return products.GetItem(p) >= q;
}
};
In der Tat ist mit PexChoose bereits das Standardverhalten für Stubs bei der Verwendung von Pex, das Testprojekt erstellt durch Pex das following-Attribut auf Assemblyebene enthält:
[assembly: PexChooseAsStubFallbackBehavior]
Der SIWareHouse-Typ wird vom Framework Pex Stubs generiert. Die IWareHouse-Schnittstelle implementiert. Let’s näher auf den Code, der vom Pex für SIWareHouse-Stub generiert. Der Quellcode für eine .stubx-Datei wird in einer partiellen Klasse mit dem Namen < StubsxFilename > erstellt. designer.cs, wie in Abbildung 7 dargestellt.
Abbildung 7 Pex generierte IWareHouse Stub
/// <summary>Stub of method System.Boolean
/// FabrikamSports.IWareHouse.HasInventory(
/// FabrikamSports.Product product, System.Int32 quantity)
/// </summary>
[System.Diagnostics.DebuggerHidden]
bool FabrikamSports.IWareHouse.HasInventory(
FabrikamSports.Product product, int quantity) {
StubDelegates.Func<FabrikamSports.Product, int, bool> sh
= this.HasInventory;
if (sh != (StubDelegates.Func<FabrikamSports.Product,
int, bool>)null)
return sh.Invoke(product, quantity);
else {
var stub = base.FallbackBehavior;
return stub.Result<FabrikamSports.Stubs.SIWareHouse,
bool>(this);
}
}
/// <summary>Stub of method System.Boolean
/// FabrikamSports.IWareHouse.HasInventory(
/// FabrikamSports.Product product, System.Int32 quantity)
/// </summary>
public StubDelegates.Func<FabrikamSports.Product, int, bool> HasInventory;
Stubs ein Delegatfeld öffentliche für die HasInventory-Methode erstellt und es in der Implementierung HasInventory aufgerufen. Wenn keine Implementierung zur Verfügung steht, ruft Pex die FallBackBehaviour.Result-Methode, die PexChoose verwenden würden, wenn der [Assembly: PexChooseAsStubFallbackBehavior] vorhanden ist und andernfalls löst eine StubNotImplementedException.
Um die gekürzte Implementierung von IWareHouse zu verwenden, wird ich den Komponententest parametrisiert etwas optimieren. Bereits geändert, die Order-Klasse, um eine Implementierung von IWareHouse in seinem Konstruktor erreichen zu können. Ich jetzt eine SIWareHouse-Instanz erstellen und dann, die an die Order-Klasse übergeben, damit Sie die benutzerdefinierten Implementierungen der Methoden IWareHouse verwendet. Der überarbeitete PUT ist hier dargestellt:
[PexMethod]
public bool Fill(Product product, int quantity) {
// Customize the default implementation of SIWareHouse
var wareHouse = new SIWareHouse() {
HasInventoryProductInt32 = (p, q) =>
PexChoose.FromCall(this).ChooseValue<bool>(
"return value")
};
var target = new Order(wareHouse);
// act
bool result = target.Fill(product, quantity);
return result;
}
Stubs stellt Standardimplementierungen für die Methoden Stub tatsächlich automatisch, so dass Sie einfach die PUT ohne Änderungen auch ausgeführt haben, konnte.
Parametrisierte Modelle
Für eine eine präzisere Steuerung der gekürzte Implementierung unterstützt Pex ein Konzept, eine parametrisierte Modell genannt wird. Dies ist eine Möglichkeit, Stubs zu schreiben, die nicht über einen bestimmten festen Verhalten verfügen. Die Abstraktion, die durch dieses Konzept Pex stellt ist, dass der Entwickler nicht die Variationen der Implementierung kümmern muss. Pex wird untersucht, unterschiedlichen Rückgabewerte der Methode abhängig, wie Sie durch den Code unter Test verwendet werden. Parametrisierte Modelle sind ein leistungsfähiges Feature, mit dem Sie vollständige Kontrolle über wie die Stubs und gleichzeitig die Pex die variant-Werte für die Eingabeparameter auswerten lassen verarbeitet werden sollen.
Ein parametrisierter Modell für IWareHouse sieht wie der Code im Abbildung 8 .
Abbildung 8 parametrisierte Model des IWareHouse
public sealed class PWareHouse : IWareHouse {
PexChosenIndexedValue<Product, int> products;
public PWareHouse() {
this.products =
new PexChosenIndexedValue<Product, int>(
this, "Products", quantity => quantity >= 0);
}
public bool HasInventory(Product product, int quantity) {
int availableQuantity = this.products.GetItem(product);
return quantity - availableQuantity > 0;
}
public void Remove(Product product, int quantity) {
int availableQuantity =
this.products.GetItem(product);
this.products.SetItem(product,
availableQuantity - quantity);
}
}
Im Wesentlichen ich meine eigene Implementierung Stubs für IWareHouse erstellt haben, aber Beachten Sie, dass ich keine Werte für die Menge und Produkt bereitstellen. Stattdessen lassen Sie Pex, die diese Werte zu generieren. PexChosenIndexedValue ermöglicht automatisch die Werte für die Objekte nur eine gekürzte Implementierung mit variant Parameterwerten.
Der Einfachheit halber wird ich Pex die HasInventory-Implementierung des Typs IWareHouse bereitstellen können. Ich wird die OrderFactory-Klasse Code hinzufügen, die ich zuvor erstellt haben. Jedes Mal, wenn eine Order-Instanz von Pex erstellt, wird verwendet eine gekürzte Warehouse Instanz.
Moles
Ich habe bisher Schwerpunkt auf zwei Prinzipien – Umgestalten von Code erleichtern getestet werden, und verwenden Sie dann Pex zum Generieren von Komponententests. Dieser Ansatz ermöglicht es Ihnen, Ihren Code, was schließlich zu mehr verwaltbar Software zu reinigen. Umgestaltung Legacycode kann jedoch eine große Herausforderung in sich selbst sein. Es sind zahlreiche Organisations- oder technische Einschränkungen, die einen Entwickler möglicherweise verhindern Umgestaltung den aktuellen Quellcode. Wie behandeln Sie dies?
In Szenarien, in denen Legacycode Refactor schwierig ist, sollte der Ansatz ausreichende Komponententests für die Geschäftslogik mindestens zu erstellen, so dass Sie die Stabilität des Systems jedes Moduls überprüfen können. Um eine Weile ist ein Mock Frameworks wie TypeMock (learn.typemock.com) wurden. Sie können Sie Komponententests zu erstellen, ohne die Codebasis tatsächlich ändern. Dieser Ansatz beweist sehr von Vorteil, insbesondere für große Legacy codebases.
Im Lieferumfang von Pex ist ein Feature namens „ Moles, mit dem Sie die gleichen Ziele erreichen kann. Sie können Komponententests für Legacycode Pex generieren, ohne tatsächlich Umgestaltung von Quellcode. Moles wirklich sollen testen andernfalls untestable Teile des Systems, z. B. versiegelte Klassen und statische Methoden.
Moles funktionieren auf ähnliche Weise wie Stubs: Pex Mole-Typen, die Delegaten-Eigenschaften für jede Methode verfügbar machen Code generiert. Sie können anfügen ein Delegaten, und fügen Sie eine Mole. An dieser Stelle erhalten alle benutzerdefinierten Delegaten einrichten wie von Zauberhand vom Profiler Pex eingegliedert.
Pex wird automatisch in der Datei .stubx Moles für alle statischen, versiegelte und öffentliche Schnittstellen wie angegeben erstellt. Eine Pex Mole sieht ähnlich wie ein Stubs geben (Siehe Codedownload ein Beispiel für).
Die Verwendung von Moles ist recht einfach. Sie können Implementierungen für die Mole-Methoden bereitstellen und verwenden Sie in Ihrem PUT. Beachten Sie, dass da Mole gekürzte Implementierungen während der Laufzeit einfügt, Sie keinen so ändern Sie Ihre Codebasis überhaupt zu Moles mit Komponententests generieren können.
Verwenden Sie Let’s Moles, auf die älteren Fill-Methode:
public class Order {
public bool Fill(Product product, int quantity) {
// Check if warehouse has any inventory
Warehouse wareHouse = new Warehouse();
if (wareHouse.HasInventory(product, quantity)) {
// Subtract the quantity from the product
// in the warehouse
wareHouse.Remove(product, quantity);
return true;
}
return false;
}
}
Ich erstelle eine PUT für die Fill-Methode, die die Typen Mole nutzt (siehe Abbildung 9 ).
Abbildung 9 mithilfe von Typen auf Fill Mole
[PexMethod]
public bool Fill([PexAssumeUnderTest]Order target,
Product product, int quantity) {
var products = new PexChosenIndexedValue<Product, int>(
this, "products");
// Attach a mole of WareHouse type
var wareHouse = new MWarehouse {
HasInventoryProductInt32 = (p, q) => {
Assert.IsNotNull(p);
return products.GetItem(p) >= q;
}
};
// Run the fill method for the lifetime of the mole
// so it uses MWareHouse
bool result = target.Fill(product, quantity);
return result;
}
MWareHouse ist ein Mole-Typ, der beim Generieren der Stubs und Moles über die .stubx-Datei automatisch durch Pex erstellt wurde. Ich eine benutzerdefinierte Implementierung für die HasInventory-Delegat des Typs MWareHouse bereitstellen, und rufen Sie dann die Fill-Methode. Beachten Sie, dass nirgends ich eine Implementierung des Warehouse-Objekts, um die Konstruktoren der Order-Typs bereitstellen. Pex wird die MWareHouse-Instanz zur Laufzeit an den Order-Typ anfügen. Code innerhalb des Blocks wird während der Lebensdauer der PUT Typimplementierung MWareHouse nutzen, überall im legacy Code eine Warehouse-Implementierung erfordert.
Wenn Pex herkömmlichen Komponententests, die Moles verwenden generiert, hängt er das Attribut [HostType(“pex”)] an, so dass Sie mit der Pex-Profiler ausgeführt werden, wodurch das Moles aktiv wird.
Zusammenfassung
Ich erläutert über die verschiedenen Features von Pex und deren Verwendung. Nun ist es Zeit, um tatsächlich die PUT ausführen und sehen Sie sich die Ergebnisse. Eine Untersuchung für die Fill-Methode von Auftrag ausführen möchten, klicken Sie mit der rechten Maustaste auf die PUT und Pex Exploration ausführen wählen. Optional können Sie die Untersuchung auf eine Klasse oder das gesamte Projekt ausführen.
Während Pex Untersuchung ausgeführt wird, wird eine partielle Klasse zusammen mit der PUT-Klassendatei erstellt. Diese partielle Klasse enthält alle standardmäßigen Komponententests, die Pex für die PUT generiert. Für die Fill-Methode generiert Pex standard Komponententests mit verschiedenen Test Eingaben. Die Tests sind in Abbildung 10 dargestellt.
Abbildung 10 Pex generierten Tests
[TestMethod]
[PexGeneratedBy(typeof(OrderTest))]
public void Fill15()
{
Warehouse warehouse;
Order order;
Product product;
bool b;
warehouse = new Warehouse();
order = OrderFactory.Create((IWareHouse)warehouse);
product = new Product("Base ball", (string)null);
b = this.Fill(order, product, 0);
Assert.AreEqual<bool>(true, b);
}
[TestMethod]
[PexGeneratedBy(typeof(OrderTest))]
public void Fill16()
{
Warehouse warehouse;
Order order;
Product product;
bool b;
warehouse = new Warehouse();
order = OrderFactory.Create((IWareHouse)warehouse);
product = new Product("Basket Ball", (string)null);
b = this.Fill(order, product, 0);
Assert.AreEqual<bool>(true, b);
}
[TestMethod]
[PexGeneratedBy(typeof(OrderTest))]
public void Fill17()
{
Warehouse warehouse;
Order order;
Product product;
bool b;
warehouse = new Warehouse();
order = OrderFactory.Create((IWareHouse)warehouse);
product = new Product((string)null, (string)null);
b = this.Fill(order, product, 1);
Assert.AreEqual<bool>(false, b);
}
Hier sehen Sie sich darauf ist die Variationen für den Product-Typ. Obwohl ich keine Factory bereitgestellt, konnte Pex Varianten für den Typ zu erstellen.
Beachten Sie außerdem, dass die generierten Tests eine Assertion enthält. Wenn ein PUT Werte zurückgibt, bettet Pex die Werte, die beim Test Generation Zeit in der generierten Testcode als Assertionen zurückgegeben wurden. Daher ist der generierte Test oft wichtige Änderungen in der Zukunft zu erkennen, auch wenn andere Assertionen in den Programmcode verletzen, oder nicht dazu führen, Ausnahmen auf der Ebene des Ausführungsmoduls dass.
Das Pex Untersuchung Ergebnisfenster enthält (siehe Abbildung 11 ) Details die Komponententests von Pex generiert. Darüber hinaus werden Informationen zu den Factorys, die pex erstellt, und die Ereignisse, die während der Untersuchung aufgetreten. Beachten Sie, dass im Abbildung 11-, dass zwei Tests fehlgeschlagen ist. Pex wird eine NullReferenceException für beide. Dies kann ein häufiges Problem sein, wo Du fehlst, platzieren Gültigkeitsprüfungen in Codepfade, die schließlich zu Ausnahmen führen könnten, wenn in der Produktion ausgeführt.
Abbildung 11 Pex Exploration Ergebnisse
Pex nicht nur die Tests generiert, sondern wird auch der Code für eine Verbesserung analysiert. Es bietet eine Reihe von Vorschlägen, die den Code selbst stabiler machen können. Diese Vorschläge handelt es sich nicht nur beschreibende Meldung, sondern tatsächlichen Code für den Problembereich. Durch Klicken auf eine Schaltfläche wird den Code Pex in der aktuellen Quelldatei einfügt. Wählen Sie den fehlgeschlagenen Test im Fenster Ergebnis Untersuchung Pex aus. In der unteren rechten Ecke wird eine Schaltfläche mit dem Titel hinzufügen Precondition angezeigt. Durch Klicken auf diese Schaltfläche wird den Code in der Quelldatei hinzugefügt.
Diese generierten Tests sind normale MSTest-Komponententests und aus den-Editor von Visual Studio Test ebenfalls ausgeführt werden können. Wenn Sie den Editor zu öffnen, werden alle Pex generierten Tests als standard Komponententests verfügbar. Pex kann ähnliche Tests für andere Frameworks wie NUnit und xUnit-Komponententests generieren.
Pex verfügt außerdem über integrierte Unterstützung für das Generieren von Berichten für die Abdeckung. Diese Berichte enthalten umfassende Details, um die dynamische Abdeckung, die für den Code unter Test. Sie können Berichte Pex Optionen im Menü "Extras" von Visual Studio aus aktivieren und dann diese öffnen, indem Sie auf Ansichten | Bericht in der Menüleiste nach Abschluss eine Untersuchung von Pex.
Machen Ihre zukünftige Tests bereit
Bisher haben Sie gesehen, wie Pex Codeabdeckung für Legacycode mit geringfügigen Umgestaltung des Quellcodes generieren konnte. Der Vorteil des Pex besteht darin, dass es den Entwickler vom Schreiben von Komponententests nimmt und generiert diese automatisch, wodurch den gesamten testen Aufwand verringert.
Einen der Punkte größere Schwierigkeiten beim Komponententests wird die Testsuite selbst verwalten. Während Sie in einem Projekt, stellen Sie in der Regel viele Änderungen an vorhandenem Code. Da die Komponententests den Quellcode abhängig sind, wirkt sich die entsprechende Komponententests auf alle Codeänderung. Sie können die Codeabdeckung reduzieren oder aufteilen. Über einen bestimmten Zeitraum wird halten die Testsuite live eine Herausforderung.
Pex ist praktisch in dieser Situation. Da Pex auf ein unsystematische Ansatz basiert, kann für alle neuen Änderungen in der Codebasis zu suchen und darauf basierende neue Testfälle erstellen.
Der wichtigste Zweck der Regressionstests erkennen, ob Änderungen oder Hinzufügungen an vorhandenem Code die Codebasis, beeinträchtigt haben ist entweder durch die Einführung von funktionalen Bugs oder Erstellen eines neuen Fehlerbedingungen.
Pex kann eine Regression Testsuite automatisch generieren. Wenn diese Regression Testsuite in der Zukunft ausgeführt wird, erkennt er grundlegend geändert, Assertionen im Programmcode zu einem Ausfall verursachen, verursachen, die Ausnahmen auf der Ebene des Ausführungsmoduls (NullReferenceException) oder verursachen, Assertionen, in die generierten Tests fehlschlagen eingebettet. Bei jeder Ausführung eine Pex Untersuchung frische, werden Komponententests für den Code unter Beobachtung generiert. Jede Änderung im Verhalten wird durch Pex und die entsprechende Einheit Tests für ihn generiert werden übernommen.
Änderung ist unvermeidlich
Über einen bestimmten Zeitraum realisiert das Entwicklerteam bei Fabrikam, dass es sinnvoll sein, eine Product-Klasse hinzugefügt, damit das Unternehmen hinzufügt, neue Produkte zu Ihren Katalog Sie eindeutig identifiziert werden können ProductId-Attribut vorgenommen.
Außerdem wurde die Order-Klasse nicht speichern die Aufträge in einem Datenspeicher so die Order-Klasse eine neue private Methode SaveOrders hinzugefügt wurde. Diese Methode wird von der Fill-Methode aufgerufen werden, wenn das Produkt einige Bestand hat.
Die geänderte Fill-Methode Klasse sieht folgendermaßen aus:
public bool Fill(Product product, int quantity) {
if (product == null) {
throw new ArgumentException();
}
if (this.orderWareHouse.HasInventory(product, quantity)) {
this.SaveOrder(product.ProductId, quantity);
this.orderWareHouse.Remove(product, quantity);
return true;
}
return false;
}
Da die Signatur der Fill-Methode nicht geändert wurde, muss ich die PUT überarbeiten. Ich führen Sie einfach die Pex Exploration erneut aus. Pex wird die Untersuchung ausgeführt, aber diesmal Eingaben mithilfe der neuen Product-Definition, verwenden die ProductId ebenfalls generiert. Er generiert a frische Testsuite, unter Berücksichtigung der Änderungen an die Fill-Methode. Die Codeabdeckung aus um 100 Prozent betragen geht – sicherstellen, dass alle neuen und vorhandenen Codepfade ausgewertet wurden.
Zusätzliche Komponententests von Pex So testen Sie die Abweichungen des Feldes ProductId hinzugefügt und die Änderungen an die Fill-Methode generiert werden (siehe Abbildung 12 ). Hier PexChooseStubBehavior legt das fallback-Verhalten für die Stubs, anstatt eine StubNotImplementedException nur auszulösen, rufen gekürzte Methode PexChoose, um die möglichen Rückgabewerte bereitzustellen. Die Tests in Visual Studio ausgeführt wird, stammt die Codeabdeckung aus erneut auf 100 Prozent.
Abbildung 12 zusätzliche Pex generierte Komponententests
[TestMethod]
[PexGeneratedBy(typeof(OrderTest))]
public void Fill12()
{
using (PexChooseStubBehavior.NewTest())
{
SIWareHouse sIWareHouse;
Order order;
Product product;
bool b;
sIWareHouse = new SIWareHouse();
order = OrderFactory.Create((IWareHouse)sIWareHouse);
product = new Product((string)null, (string)null);
b = this.Fill(order, product, 0);
Assert.AreEqual<bool>(false, b);
}
}
[TestMethod]
[PexGeneratedBy(typeof(OrderTest))]
public void Fill13()
{
using (PexChooseStubBehavior.NewTest())
{
SIWareHouse sIWareHouse;
Order order;
Product product;
bool b;
sIWareHouse = new SIWareHouse();
order = OrderFactory.Create((IWareHouse)sIWareHouse);
product = new Product((string)null, (string)null);
IPexChoiceRecorder choices = PexChoose.NewTest();
choices.NextSegment(3)
.OnCall(0,
"SIWareHouse.global::FabrikamSports.IWareHouse.HasInventory(Product, Int32)")
.Returns((object)true);
b = this.Fill(order, product, 0);
Assert.AreEqual<bool>(true, b);
}
}
Danksagung
Ich möchte Danke, dass Peli de Halleux und Nikolai Tillman ermutigen, mich, diese Artikel, die besonderen Dank an Peli für seinen tireless Unterstützung, wertvolle Kommentare und ausführlichen Prüfung zu schreiben.
Nikhil Sachdeva ist ein Software Development Engineer für das OCTO-SE-Team bei Microsoft. Sie erreichen ihn unter blogs.msdn.com/erudition . Sie können auch Ihre Abfragen um Pex social.msdn.microsoft.com/Forums/en/pex/threads buchen.