paint-brush
Testen in Java: Die Schlüsselkonzepte [Teil 1: Unit-Tests]von@gromspys
1,628 Lesungen
1,628 Lesungen

Testen in Java: Die Schlüsselkonzepte [Teil 1: Unit-Tests]

von Sergei Korneev10m2023/11/14
Read on Terminal Reader

Zu lang; Lesen

In diesem Artikel werden wir die Schlüsselkonzepte des Java-Unit-Tests untersuchen, einschließlich einfacher Codebeispiele, Parametrisierung, Ausnahmetests und Anmerkungen.
featured image - Testen in Java: Die Schlüsselkonzepte [Teil 1: Unit-Tests]
Sergei Korneev HackerNoon profile picture

Unit-Tests sind eine grundlegende Praxis in der Softwareentwicklung, die die Zuverlässigkeit und Korrektheit Ihres Codes gewährleistet. In diesem Artikel werden wir die Schlüsselkonzepte des Java-Unit-Tests untersuchen, einschließlich einfacher Codebeispiele, Parametrisierung, Ausnahmetests, Annotationen wie @Before , @BeforeEach , @After und @AfterEach sowie die Verwendung von Mocks und Stubs .


Außerdem stellen wir die beliebte Mockito-Bibliothek zum Mocking vor und geben Anleitungen zum Messen der Codeabdeckung mithilfe von Maven-Plugins.

Die Bedeutung von Unit-Tests

Unter Unit-Tests versteht man das isolierte Testen einzelner Komponenten oder Codeeinheiten, um deren Korrektheit zu überprüfen. Das Hauptziel besteht darin, Fehler frühzeitig im Entwicklungsprozess zu erkennen und zu beheben und sicherzustellen, dass sich jede Codeeinheit wie erwartet verhält.


Beginnen wir mit der Erstellung einer einfachen Rechnerklasse, die wir für unsere Beispiele verwenden können. Die Calculator Klasse enthält grundlegende arithmetische Operationen:

 public class Calculator { public int add(int a, int b) { return a + b; } public int subtract(int a, int b) { return a - b; } public int multiply(int a, int b) { return a * b; } public int divide(int a, int b) { return a / b; } }

Einen einfachen Unit-Test schreiben

Um einen Komponententest für unsere Calculator Klasse zu erstellen, können Sie das JUnit-Framework verwenden, ein beliebtes Test-Framework für Java. Schreiben wir einen einfachen Komponententest für die add Methode:

 import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class CalculatorTest { @Test public void testAdd() { Calculator calculator = new Calculator(); int result = calculator.add(2, 3); assertEquals(5, result); } }

In diesem Test importieren wir die notwendigen JUnit-Klassen und kommentieren die Testmethode mit @Test . Anschließend erstellen wir eine Instanz der Calculator Klasse und stellen sicher, dass das Ergebnis der add Methode gleich dem erwarteten Wert (5) ist.

Parametrisierte Tests

Mit parametrisierten Tests können Sie dieselbe Testlogik mit mehreren Eingabedatensätzen ausführen. Dies ist nützlich, um eine Methode mit verschiedenen Eingabewerten zu testen. Dazu können Sie in JUnit die Annotation @ParameterizedTest verwenden und die Eingabewerte und erwarteten Ergebnisse als Parameter bereitstellen. Hier ist ein Beispiel:

 import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import static org.junit.jupiter.api.Assertions.assertEquals; public class CalculatorTest { @ParameterizedTest @CsvSource({"2, 3, 5", "4, 7, 11", "0, 0, 0"}) public void testAdd(int a, int b, int expected) { Calculator calculator = new Calculator(); int result = calculator.add(a, b); assertEquals(expected, result); } }

In diesem Beispiel können wir mit der Annotation @ParameterizedTest mehrere Sätze von Eingabewerten und erwarteten Ergebnissen in einem CSV-Format bereitstellen. Die Testmethode wird für jede Kombination ausgeführt, um die Richtigkeit der add Methode sicherzustellen.

Ausnahmetests

Das Testen von Ausnahmen ist von entscheidender Bedeutung, um sicherzustellen, dass Ihr Code Fehler angemessen behandelt. Um Ausnahmefälle zu testen, können Sie die Annotation @Test zusammen mit der von JUnit bereitgestellten Methode „ assertThrows “ verwenden.


Hier ist ein Beispiel:

 javaCopy codeimport org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertThrows; public class CalculatorTest { @Test public void testDivideByZero() { Calculator calculator = new Calculator(); assertThrows(ArithmeticException.class, () -> calculator.divide(5, 0)); } }

In diesem Test überprüfen wir, ob die Division einer Zahl durch Null in der Calculator Klasse eine ArithmeticException auslöst.

Verwenden von Anmerkungen: @Before, @BeforeEach, @After und @AfterEach

Anmerkungen wie @Before , @BeforeEach , @After und @AfterEach werden zum Einrichten und Abbauen der Testumgebung verwendet. Diese Anmerkungen helfen bei der Verwaltung häufiger Testinitialisierungs- und Bereinigungsaufgaben.


  • @Before und @After werden einmal vor bzw. nach allen Testmethoden in der Testklasse ausgeführt.
  • @BeforeEach und @AfterEach werden vor und nach jeder Testmethode ausgeführt.


Hier ist ein Beispiel:

 import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class CalculatorTest { private Calculator calculator; @BeforeEach public void setUp() { calculator = new Calculator(); } @AfterEach public void tearDown() { calculator = null; } @Test public void testAdd() { int result = calculator.add(2, 3); assertEquals(5, result); } @Test public void testSubtract() { int result = calculator.subtract(5, 3); assertEquals(2, result); } }

In diesem Beispiel wird die setUp Methode mit @BeforeEach annotiert und initialisiert das Calculator Objekt vor jedem Test. Die mit @AfterEach annotierte Methode tearDown bereinigt Ressourcen nach jedem Test.

Mocks und Stubs

Mocks und Stubs sind beim Unit-Testen unerlässlich, wenn Sie den zu testenden Code von externen Abhängigkeiten wie Datenbanken oder Webdiensten isolieren möchten. Sie ermöglichen Ihnen, das Verhalten dieser Abhängigkeiten zu simulieren.


  • Mock: Ein Mock ist ein Objekt, das das Verhalten eines realen Objekts nachahmt, aber unter Ihrer Kontrolle steht. Es zeichnet Interaktionen auf und ermöglicht Ihnen die Überprüfung, ob bestimmte Methoden aufgerufen werden.


  • Stub: Ein Stub stellt vorgefertigte Antworten auf Methodenaufrufe bereit. Es gibt vordefinierte Daten zurück und wird verwendet, um bestimmte Verhaltensweisen eines externen Systems zu simulieren.


Erweitern wir die Calculator Klasse um eine Methode, die eine externe Abhängigkeit beinhaltet, und erstellen wir dann einen Stub, um diese Abhängigkeit zu simulieren.

 public class Calculator { private ExternalService externalService; public Calculator(ExternalService externalService) { this.externalService = externalService; } public int performComplexCalculation(int a, int b) { int result = externalService.multiply(a, b); return result + externalService.getConstant(); } }

In dieser aktualisierten Calculator Klasse verlässt sich performComplexCalculation auf einen ExternalService zur Multiplikation und zum Erhalten eines konstanten Werts.


So können Sie einen Stub erstellen, um die Calculator Klasse zu testen, ohne vom tatsächlichen ExternalService abhängig zu sein.

 import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.*; public class CalculatorTest { @Test public void testPerformComplexCalculation() { // Create a stub for ExternalService ExternalService externalServiceStub = new ExternalService() { @Override public int multiply(int a, int b) { return a * b; } @Override public int getConstant() { return 5; // Stubbed constant value } }; // Create the Calculator with the stubbed ExternalService Calculator calculator = new Calculator(externalServiceStub); // Test the performComplexCalculation method int result = calculator.performComplexCalculation(2, 3); assertEquals(11, result); // 2*3 + 5 (constant) = 11 } }

In diesem Test wird ExternalService innerhalb der Testmethode stubbed, indem eine anonyme Klasse erstellt wird, die die erforderlichen Methoden überschreibt. Auf diese Weise wird die Calculator Testmethode unabhängig von der tatsächlichen Implementierung von ExternalService ausgeführt.


Stubs sind nützlich, um das Verhalten externer Systeme oder Abhängigkeiten zu simulieren, um die spezifische Funktionalität einer Klasse oder Methode zu isolieren und zu testen. Dadurch können Sie das Verhalten des Stubs steuern und sich auf die zu testende Einheit konzentrieren, ohne dass tatsächlich externe Dienste erforderlich sind.

Wir stellen Mockito zum Verspotten vor

Mockito ist eine beliebte Java-Bibliothek zum Erstellen und Verwalten von Scheinobjekten. Nehmen wir an, wir haben eine PaymentService Klasse, die mit einem externen Zahlungsgateway interagiert. Wir können Mockito verwenden, um ein Mock für das Zahlungsgateway zu erstellen:

 import org.junit.jupiter.api.Test; import static org.mockito.Mockito.*; public class PaymentServiceTest { @Test public void testProcessPayment() { PaymentGateway paymentGateway = mock(PaymentGateway.class); PaymentService paymentService = new PaymentService(paymentGateway); when(paymentGateway.charge(100.0)).thenReturn(true); boolean result = paymentService.processPayment(100.0); assertTrue(result); verify(paymentGateway, times(1)).charge(100.0); } }

In diesem Beispiel erstellen wir ein simuliertes PaymentGateway und verwenden die when Methode, um sein Verhalten zu definieren. Anschließend rufen wir die Methode processPayment der Klasse PaymentService auf und überprüfen, ob die Methode charge mit den erwarteten Parametern aufgerufen wurde.

Codeabdeckung und Maven-Plugin

Die Codeabdeckung misst den Prozentsatz der Codezeilen, Verzweigungen oder Anweisungen, die von Ihren Komponententests ausgeführt werden. Es hilft Ihnen, ungetesteten Code und Bereiche zu identifizieren, die möglicherweise zusätzliche Tests erfordern.


Maven ist ein beliebtes Build-Tool für Java-Projekte, und Sie können mithilfe von Plugins wie JaCoCo eine Code-Coverage-Analyse in Ihr Maven-Projekt integrieren. So fügen Sie JaCoCo zu Ihrem Projekt hinzu:


  1. Öffnen Sie pom.xml Datei Ihres Projekts.
  2. Fügen Sie die JaCoCo Maven-Plugin-Konfiguration hinzu:
 <build> <plugins> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.7</version> <executions> <execution> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>report</id> <phase>test</phase> <goals> <goal>report</goal> </goals> </execution> </executions> </plugin> </plugins> </build>


Führen Sie Ihren Maven-Build mit dem folgenden Befehl aus:

 mvn clean verify


Nachdem Sie den Build ausgeführt haben, finden Sie den Codeabdeckungsbericht in der Datei target/site/jacoco/index.html . Dieser Bericht liefert detaillierte Informationen über die Abdeckung Ihres Codes durch Ihre Unit-Tests.

Abschluss

Das Testen von Java-Einheiten ist eine wesentliche Methode, um die Zuverlässigkeit und Korrektheit Ihres Codes sicherzustellen. Mit Tools wie JUnit und Mockito können Sie effektive Unit-Tests und Mocks für Ihre Komponenten schreiben.


Durch die Integration der Codeabdeckungsanalyse mit Maven und JaCoCo können Sie sicherstellen, dass Ihre Tests einen erheblichen Teil Ihrer Codebasis abdecken. Regelmäßiges Testen und Pflegen von Unit-Tests hilft Ihnen dabei, qualitativ hochwertige und robuste Java-Anwendungen zu erstellen.


Im nächsten Teil dieser Serie befassen wir uns mit Integrationstests, einem kritischen Aspekt des Softwaretests, bei dem die Interaktionen zwischen verschiedenen Komponenten und Diensten getestet werden, um sicherzustellen, dass das Gesamtsystem korrekt funktioniert.


Seien Sie gespannt auf den zweiten Teil, in dem wir in die aufregende Welt des Integrationstests eintauchen!