Seien Sie am 30. April dabei: Vorstellung von Parasoft C/C++test CT für kontinuierliche Tests und Compliance-Exzellenz | Registrierung

Verspotten in Java: So automatisieren Sie einen Java-Komponententest, einschließlich Verspottung und Behauptungen

Kopfschuss von Brian McGlauflin,
19. Juli 2018
6 min lesen

Durch die Reduzierung der mit Mocking verbundenen Komplexität ermöglicht Ihnen Parasoft Jtest das schnelle und einfache Schreiben von Komponententests. Erfahren Sie mehr darüber, wie Sie einen Java-Komponententest automatisieren, einschließlich Mocking und Assertions.

Gutes Komponententests sind eine großartige Möglichkeit, um sicherzustellen, dass Ihr Code heute und auch in Zukunft funktioniert. Eine umfassende Testsuite mit guter codebasierter und verhaltensbasierter Abdeckung kann einem Unternehmen viel Zeit und Kopfschmerzen ersparen. Dennoch kommt es nicht selten vor, dass bei Projekten nicht genügend Tests geschrieben werden. Tatsächlich haben sich einige Entwickler sogar komplett gegen ihren Einsatz ausgesprochen.

Was macht einen guten Unit-Test aus?

Es gibt viele Gründe, warum Entwickler nicht genügend Komponententests schreiben. Einer der Hauptgründe ist die Zeit, die sie für den Bau und die Wartung benötigen, insbesondere bei großen, komplexen Projekten. In komplexen Projekten muss ein Komponententest häufig viele Objekte instanziieren und konfigurieren. Das Einrichten nimmt viel Zeit in Anspruch und kann den Test komplexer (oder komplexer) machen als den Code, den er selbst testet.

Schauen wir uns ein Beispiel in Java an:

Öffentlichkeit LoanResponse requestLoan (LoanRequest Leihantrag, Kreditstrategie Strategie)
{     LoanResponse response = new LoanResponse();     response.setApproved(true);     if (loanRequest.getDownPayment().compareTo(loanRequest.getAvailableFunds()) > 0) {        response.setApproved(false);        response.setMessage("error.insufficient.funds.for.down.payment");        return response;     }     if (strategy.getQualifier(loanRequest) < strategy.getThreshold(adminManager)) {         response.setApproved(false);         response.setMessage(getErrorMessage());     }     return response; }

Hier haben wir eine Methode, die a verarbeitet Kreditanfrage, erzeugen a Kreditantwort. Beachten Sie das Kreditstrategie Argument, das verwendet wird, um die zu verarbeiten Kreditanfrage. Das Strategieobjekt kann komplex sein - es kann auf eine Datenbank oder ein externes System zugreifen oder a auslösen LaufzeitAusnahme. Um einen Test für zu schreiben Darlehen anfordern()Ich muss mir Sorgen machen, welche Art von Kreditstrategie Ich teste mit und ich muss wahrscheinlich meine Methode mit einer Vielzahl von testen Kreditstrategie Implementierungen und Kreditanfrage Konfigurationen.

Ein Unit Test fürDarlehen anfordern()könnte so aussehen:

@Test public void testRequestLoan() throws Throwable {    // Set up objects    DownPaymentLoanProcessor processor = new DownPaymentLoanProcessor();    LoanRequest loanRequest = LoanRequestFactory.create(1000, 100, 10000);    LoanStrategy strategy = new AvailableFundsLoanStrategy();    AdminManager adminManager = new AdminManagerImpl();    underTest.setAdminManager(adminManager);    Map<String, String> parameters = new HashMap<>();    parameters.put("loanProcessorThreshold", "20");    AdminDao adminDao = new InMemoryAdminDao(parameters);    adminManager.setAdminDao(adminDao);    // Call the method under test    LoanResponse response = processor.requestLoan(loanRequeststrategy);    // Assertions and other validations 

Wie Sie sehen können, gibt es einen ganzen Abschnitt meines Tests, in dem nur Objekte erstellt und Parameter konfiguriert werden. Es war nicht offensichtlich, das zu betrachten Darlehen anfordern() Methode, welche Objekte und Parameter eingerichtet werden müssen. Um dieses Beispiel zu erstellen, musste ich den Test ausführen, eine Konfiguration hinzufügen, dann erneut ausführen und den Vorgang immer wieder wiederholen. Ich musste zu viel Zeit damit verbringen, herauszufinden, wie man das konfiguriert Admin-Manager und dem Kreditstrategie anstatt mich auf meine Methode und das zu konzentrieren, was dort getestet werden musste. Und ich muss meinen Test noch erweitern, um mehr abzudecken Kreditanfrage Fälle, mehr Strategien und mehr Parameter für AdminDao.

Durch die Verwendung realer Objekte zum Testen überprüft mein Test außerdem mehr als nur das Verhalten von Darlehen anfordern() - Ich bin abhängig vom Verhalten von VerfügbareMittelDarlehenStrategie, AdminManagerImpl und AdminDao damit mein Test läuft. Tatsächlich teste ich auch diese Klassen. In einigen Fällen ist dies wünschenswert, in anderen Fällen jedoch nicht. Wenn sich eine dieser anderen Klassen ändert, schlägt der Test möglicherweise fehl, obwohl das Verhalten von Darlehen anfordern() hat sich nicht geändert. Für diesen Test möchten wir die zu testende Klasse lieber von ihren Abhängigkeiten isolieren.

Was ist Spott in Java?

Eine Lösung für das Komplexitätsproblem besteht darin, diese komplexen Objekte zu verspotten. In diesem Beispiel verwende ich zunächst ein Modell für das Kreditstrategie Parameter:

@Prüfung

öffentliche Leere testRequestLoan () wirft Werfbar
    // Objekte einrichten 
    AnzahlungDarlehenProzessor Prozessor = neu DownPaymentLoanProcessor (); LoanRequest Leihantrag = LoanRequestFactory.create (1000, 100, 10000); LoanStrategy Strategie = Mockito.mock (LoanStrategy.Klasse); Mockito.when (Strategie.getQualifier (any (LoanRequest.Klasse))). thenReturn (20.0d); Mockito.when (Strategie.getThreshold (beliebig (AdminManager).Klasse))). thenReturn (20.0d);

    // Rufe die zu testende Methode auf
    Kreditantwort Antwort = process.requestLoan (LeihantragStrategie);

    // Behauptungen und andere Validierungen
}

Schauen wir uns an, was hier passiert. Wir erstellen eine verspottete Instanz von Kreditstrategie Verwendung von Mockito.mock (). Da wissen wir das getQualifier () und getThreshold () Wird auf die Strategie aufgerufen, definieren wir die Rückgabewerte für diese Aufrufe mit Mockito.when (…) .thenReturn (). Für diesen Test ist es uns egal, was das ist Kreditanfrage Die Werte der Instanz sind und wir brauchen keine realen Admin-Manager mehr weil Admin-Manager wurde nur von der realen verwendet Kreditstrategie.

Außerdem, da wir keine echte verwenden KreditstrategieEs ist uns egal, was die konkreten Implementierungen von Kreditstrategie könnte tun. Wir müssen keine Testumgebungen, Abhängigkeiten oder komplexen Objekte einrichten. Wir konzentrieren uns auf das Testen Darlehen anfordern() - nicht Kreditstrategie or Admin-Manager. Der Code-Fluss der zu testenden Methode wird direkt vom Mock gesteuert.

Dieser Test ist mit Mockito viel einfacher zu schreiben, als wenn ich einen Komplex erstellen müsste Kreditstrategie Beispiel. Aber es gibt noch einige Herausforderungen:

  • Für komplexe Anwendungen erfordern Tests möglicherweise viele Verspottungen
  • Wenn Sie Mockito noch nicht kennen, müssen Sie dessen Syntax und Muster kennenlernen
  • Möglicherweise wissen Sie nicht, welche Methoden verspottet werden müssen
  • Wenn sich die Anwendung ändert, müssen auch die Tests (und Verspottungen) aktualisiert werden

Mocking-Herausforderungen mit einem Java-Unit-Test-Generator lösen

Wir haben Parasoft Jtest entwickelt, um die oben genannten Herausforderungen zu bewältigen. Das Unit-Testing-Modul Parasoft Jtest, eine Unternehmenslösung für Java-Tests, die Entwicklern hilft, die Risiken von zu verwalten Java-Softwareentwicklung.

Auf der Seite der Komponententests hilft Ihnen Parasoft Jtest dabei, einige der schwierigsten Teile beim Erstellen und Verwalten von Komponententests mit Mocks zu automatisieren. Für das obige Beispiel kann automatisch ein Test für generiert werden Darlehen anfordern() mit einem einzigen Knopfdruck, einschließlich aller Verspottungen und Validierungen, die Sie im Beispieltest sehen.


Hier habe ich die Aktion "Normal" im Parasoft Jtest verwendet Unit-Test-Assistent Symbolleiste zum Generieren des folgenden Tests:

@Test public void testRequestLoan() throws Throwable {     // Given     DownPaymentLoanProcessor underTest = new DownPaymentLoanProcessor();     // When doppelt verfügbares Vermögen = 0.0d// UTA: Standardwert doppelt Anzahlung = 0.0d// UTA: Standardwert doppelt LoanAmount = 0.0d// UTA: Standardwert     LoanRequest loanRequest = LoanRequestFactory.create(availableFundsdownPaymentloanAmount);     LoanStrategy strategy = mockLoanStrategy();     LoanResponse result = underTest.requestLoan(loanRequeststrategy);    // Then    // assertNotNull(result); }

Alle Verspottungen für diesen Test erfolgen in einer Hilfsmethode:

private statische LoanStrategy mockLoanStrategy () wirft Throwable {LoanStrategy Strategie = mock (LoanStrategy.Klasse);
    doppelt getQualifierResult = 0.0d; // UTA: Standardwert
    wann(Strategie.getQualifier (any (LoanRequest.Klasse))). thenReturn (getQualifierResult);

    doppelt getThresholdResult = 0.0d; // UTA: Standardwert
    wann(Strategie.getThreshold (beliebig (AdminManager).Klasse))). thenReturn (getThresholdResult);

    Rückkehr Strategie;; }}

Alle notwendigen Verspottungen sind für mich eingerichtet - Parasoft Jtest hat die Methodenaufrufe erkannt getQualifier () und getThreshold () und verspottete die Methoden. Sobald ich Werte in meinem Unit Test für konfiguriert habe verfügbares Vermögen, Anzahlungusw. ist der Test betriebsbereit (ich könnte auch einen parametrisierten Test für eine bessere Abdeckung generieren!). Beachten Sie auch, dass der Assistent anhand seiner Kommentare „UTA: Standardwert“ eine Anleitung gibt, welche Werte geändert werden sollen, um das Testen zu vereinfachen.

Dies spart viel Zeit beim Generieren von Tests, insbesondere wenn ich nicht weiß, was verspottet werden muss oder wie die Mockito-API verwendet wird.

Umgang mit Codeänderungen

Wenn sich die Anwendungslogik ändert, müssen sich häufig auch die Tests ändern. Wenn der Test gut geschrieben ist, sollte er fehlschlagen, wenn Sie den Code aktualisieren, ohne den Test zu aktualisieren. Die größte Herausforderung bei der Aktualisierung des Tests besteht häufig darin, zu verstehen, was aktualisiert werden muss und wie genau diese Aktualisierung durchgeführt werden muss. Wenn es viele Verspottungen und Werte gibt, kann es schwierig sein, die notwendigen Änderungen zu ermitteln.

Um dies zu veranschaulichen, nehmen wir einige Änderungen am zu testenden Code vor ::

Öffentlichkeit LoanResponse requestLoan (LoanRequest Leihantrag, Kreditstrategie Strategie) {... String Folge = Strategie.bestätigen(Leihantrag);
    if (Folge != null &&!Folge.ist leer()) {
        Antwort.setApproved (falsch);
        Antwort.setMessage (Folge);
        Rückkehr Antwort;; } ...
    Rückkehr Antwort;; }}

Wir haben eine neue Methode hinzugefügt LoanStrategy - validate ()und rufen es jetzt von Darlehen anfordern(). Der Test muss möglicherweise aktualisiert werden, um anzugeben, was bestätigen() sollte zurückkehren.

Lassen Sie uns den generierten Test im Parasoft Jtest Unit Test Assistant ausführen, ohne ihn zu ändern:

Parasoft Jtest hat das festgestellt bestätigen() wurde auf den verspotteten gerufen Kreditstrategie Argument während meines Testlaufs. Da die Methode nicht für den Mock eingerichtet wurde, empfiehlt der Assistent, den Mock zu verspotten bestätigen() Methode. Die Schnellkorrekturaktion „Mock it“ aktualisiert den Test automatisch. Dies ist ein einfaches Beispiel - aber bei komplexem Code, bei dem es nicht einfach ist, das fehlende Modell zu finden, können wir durch die Empfehlung und die schnelle Lösung viel Zeit beim Debuggen sparen.

Nachdem ich den Test mit der Schnellkorrektur aktualisiert habe, kann ich das neue Modell sehen und den gewünschten Wert für einstellen validResult:

private statische LoanStrategy mockLoanStrategy () wirft Throwable {LoanStrategy Strategie = mock (LoanStrategy.Klasse); String validResult = ""// UTA: Standardwert
    wann(Strategie.validate (any (LoanRequest.Klasse))). thenReturn (validResult);
    doppelt getQualifierResult = 20.0d; wann(Strategie.getQualifier (any (LoanRequest.Klasse))). thenReturn (getQualifierResult);

    doppelt getThresholdResult = 20.0d; wann(Strategie.getThreshold (beliebig (AdminManager).Klasse))). thenReturn (getThresholdResult);
    Rückkehr Strategie;; }}

Ich kann validateResult mit einem nicht leeren Wert konfigurieren, um den Anwendungsfall zu testen, in dem die Methode in den neuen Codeblock eingibt, oder ich kann einen leeren Wert (oder null) verwenden, um das Verhalten zu validieren, wenn der neue Block nicht eingegeben wird.

Analyse des Testflusses

Der Assistent bietet auch einige nützliche Tools zur Analyse des Testflusses. Hier ist zum Beispiel der Flussbaum für unseren Testlauf:

Der Parasoft Jtest Unit-Test-Assistent's Flow Tree, der Aufrufe anzeigt, die während der Testausführung getätigt wurden

Wenn der Test ausgeführt wurde, kann ich sehen, dass der Test ein neues Modell für erstellt hat Kreditstrategieund verspottete die bestätigen(), getQualifier () und getThreshold () Methoden. Ich kann Methodenaufrufe auswählen und (in der Variablenansicht) sehen, welche Argumente an diesen Aufruf gesendet wurden und welcher Wert zurückgegeben wurde (oder Ausnahmen ausgelöst wurden). Beim Debuggen von Tests kann dies viel einfacher zu verwenden und zu verstehen sein als das Durchsuchen von Protokolldateien.

Zusammenfassung

So können Sie viele Aspekte des Unit-Tests automatisieren. Mit Parasoft Jtest können Sie Unit-Tests mit weniger Zeit und Aufwand erstellen und so die mit Mocking verbundene Komplexität reduzieren. Es gibt auch viele andere Arten von Empfehlungen zur Verbesserung bestehender Tests auf der Grundlage von Laufzeitdaten und unterstützt parametrisierte Tests, Spring Application-Tests und PowerMock (zum Verspotten statischer Methoden und Konstruktoren). Starten Sie Ihre 14-tägige Jtest-Testversion mit vollem Funktionsumfang heute.

Verbessern Sie Unit-Tests für Java mit Automatisierung