In meinem aktuellen Projekt fällt es mir schwer, eine gute Lösung zu finden, um skalierbare Integrationstests zu erstellen, die keine Nebenwirkungen haben. Ein wenig Klarheit über die nebenwirkungsfreie Eigenschaft: Es geht hauptsächlich um die Datenbank; Nach Abschluss der Tests sollten keine Änderungen in der Datenbank vorgenommen werden (der Status sollte beibehalten werden). Vielleicht passen Skalierbarkeit und Zustandserhaltung nicht zusammen, aber ich möchte unbedingt eine bessere Lösung anstreben.
Hier ist ein typischer Integrationstest (diese Tests betreffen die Datenbankebene):
public class OrderTests {
List<Order> ordersToDelete = new ArrayList<Order>();
public testOrderCreation() {
Order order = new Order();
assertTrue(order.save());
orderToDelete.add(order);
}
public testOrderComparison() {
Order order = new Order();
Order order2 = new Order();
assertFalse(order.isEqual(order2);
orderToDelete.add(order);
orderToDelete.add(order2);
}
// More tests
public teardown() {
for(Order order : ordersToDelete)
order.delete();
}
}
Wie man sich vorstellen kann, führt dieser Ansatz zu extrem langsamen Tests. Bei den gesamten Integrationstests dauert es ungefähr 5 Sekunden, um nur einen kleinen Teil des Systems zu testen. Ich kann mir vorstellen, dass diese Zahl steigt, wenn die Abdeckung erhöht wird.
Was wäre ein anderer Ansatz, um solche Tests zu schreiben? Eine Alternative, die ich mir vorstellen kann, besteht darin, globale Variablen (innerhalb einer Klasse) zu haben, und alle Testmethoden verwenden diese Variable gemeinsam. Infolgedessen werden nur wenige Aufträge erstellt und gelöscht. was zu schnelleren Tests führt. Ich denke jedoch, dass dies ein größeres Problem mit sich bringt; Die Tests sind nicht mehr isoliert und es wird immer schwieriger, sie zu verstehen und zu analysieren.
Es kann sein, dass Integrationstests nicht so oft ausgeführt werden sollen wie Unit-Tests. daher könnte eine geringe Leistung für diese akzeptabel sein. In jedem Fall wäre es gut zu wissen, ob jemand Alternativen zur Verbesserung der Skalierbarkeit gefunden hat.
Dies ist das ewige Problem, mit dem jeder beim Schreiben von Integrationstests konfrontiert ist.
Die ideale Lösung, insbesondere wenn Sie Tests in der Produktion durchführen, besteht darin, eine Transaktion im Setup zu öffnen und im Teardown zurückzusetzen. Ich denke, das sollte Ihren Bedürfnissen entsprechen.
Wenn dies nicht möglich ist, beispielsweise wenn Sie die App von der Client-Ebene aus testen, besteht eine andere Lösung darin, eine Datenbank auf einer virtuellen Maschine zu verwenden, einen Snapshot im Setup zu erstellen und in teardown darauf zurückzukehren (dies ist nicht der Fall) Dauern Sie so lange, wie Sie vielleicht erwarten.
quelle
Integrationstests sollten immer mit dem Produktionsaufbau abgeglichen werden. In Ihrem Fall bedeutet dies, dass Sie über denselben Datenbank- und Anwendungsserver verfügen sollten. Natürlich können Sie sich aus Performancegründen für einen In-Memory-DB entscheiden.
Sie sollten jedoch niemals Ihren Transaktionsumfang erweitern. Einige Leute schlugen vor, die Kontrolle über die Transaktion zu übernehmen und sie nach den Tests zurückzusetzen. Wenn Sie dies tun, alle Entitäten (vorausgesetzt, Sie verwenden die JPA) bleiben während der gesamten Testausführung an den Persistenzkontext gebunden . Dies kann zu einigen sehr bösen Fehlern führen, die sehr schwer zu finden sind .
Stattdessen sollten Sie die Datenbank nach jedem Test manuell über eine Bibliothek wie JPAUnit löschen oder ähnlich diesem Ansatz (löschen Sie alle Tabellen JDBC verwenden).
Aufgrund Ihrer Leistungsprobleme sollten Sie die Integrationstests nicht bei jedem Build ausführen. Lassen Sie dies Ihren Continuous Integration Server tun. Wenn Sie Maven verwenden, könnte Ihnen das gefallen ausfallsichere Plug-In gefallen , mit dem Sie Ihre Tests in Unit- und Integrationstests unterteilen können.
Sie sollten auch nichts verspotten. Denken Sie daran, dass Sie Integrationstests durchführen, dh das Verhalten in der Laufzeit-Ausführungsumgebung testen.
quelle
Auf Skalierbarkeit
Ich hatte vor einiger Zeit das Problem, dass die Integrationstests zu lange dauerten und für einen einzelnen Entwickler nicht praktikabel waren, um in einem engen Regelkreis von Änderungen fortlaufend ausgeführt zu werden. Einige Strategien, um damit umzugehen, sind:
Versuchen Sie, diese Techniken zu kombinieren, um eine größere Wirkung zu erzielen.
quelle
Zu Testzwecken haben wir eine dateibasierte Bereitstellung einer SQLite-Datenbank verwendet (kopieren Sie einfach eine Ressource). Dies wurde durchgeführt, damit wir auch Schemamigrationen testen können. Soweit mir bekannt ist, sind Schemaänderungen nicht transaktional und werden daher nicht zurückgesetzt, nachdem eine Transaktion abgebrochen wurde. Wenn Sie für die Testeinrichtung nicht auf die Transaktionsunterstützung angewiesen sind, können Sie das Verhalten von Transaktionen in Ihrer Anwendung ordnungsgemäß testen.
quelle