Qualitätssicherung, also Testing einer Anwendung ist ein entscheidender Bestandteil des Software Developments. Hier werden wir verschiedene Frameworks für das Software Testing vorstellen und Beispiele für deine Application bieten. Wir werden KoTest und JUnit für das Unit Testing, Karate für API-/E2E-Tests und Gatling für die Load Tests behandeln.
Unit Testing
In unseren Beispielen testen wir eine einfache Addition zweier Integer-Zahlen mit dem Taschenrechner. Beide Beispiel-Frameworks, bieten hier eine klare und ausdrucksstarke Syntax für die Testfallbeschreibung.
KoTest: Unit Testing mit Kotlin
KoTest ist ein modernes Framework, das sich ideal für das Testen von Kotlin-Anwendungen eignet. Es ermöglicht das einfache Schreiben von Unit Tests.
Hier ist ein Beispiel für die Verwendung von KoTest:
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
class CalculatorSpec : StringSpec({
"Adding two numbers should return the correct sum" {
val result = Calculator.add(2, 3)
result shouldBe 5
}
})
JUnit: Klassisches Unit Testing für Java
JUnit ist ein bewährtes Framework für das Unit Testing von Java-Anwendungen. Es bietet eine breite Unterstützung und wird in vielen Projekten verwendet.
Hier ist ein Beispiel:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class CalculatorTest {
@Test
void testAddition() {
int result = Calculator.add(2, 3);
assertEquals(5, result);
}
}
An diesen illustrativen Taschenrechner-Beispielen zeigt sich die Mächtigkeit der Unit Tests. Wichtig ist hier anzumerken dass es, gerade im Unit und auch später beim E2E-Testing, diverse Tests auszuführen gibt um viele mögliche Edge-cases (also Randbedingungen) zu überprüfen. Bspw.:
- Keine negativen Zahlen oder Fließkommazahlen werden überprüft.
- Der Umgang mit Buchstaben oder anderen Sonderzeichen wird auch nicht getestet.
- Mathematische Gesetze finden ebenfalls keine Berücksichtigung, wie:
- Klammerregelen
- Genauigkeit der Exponential-Funktion oder Logarithmen
- Nicht-funktionale Anforderungen sollten auch auch Gegenstand des Testings sein:
- Copy & Paste
- Tausender-/Dezimaltrennzeichens (“.” vs. “,” ; auch “locale” genannt)
Erst wenn alle diese Genannten, und noch viele weitere Tests, zusammen kommen findet sich eine zufriedenstellende Testabdeckung. Des Weiteren, lohnt es sich auch hier schon sich bestimmten Fragestellungen zu widmen um auch ggf. Software Requirements zu challengen oder schärfen, z.B.
- Wie viele Zeichen/Ziffern soll der Taschenrechner abbilden/darstellen können?
- Welche für “Rundungsbedingungen” / Präzision dürfen / darf hier gelten?
Solche Fragen müssen auf jeder Ebene des Testings berücksichtigt werden.
End-to-End-Testing mit Karate: API-Tests in natürlicher Sprache
Karate ist ein Framework, das sich auf API-Tests und End-to-End-Tests von Webanwendungen spezialisiert hat. Es ermöglicht das Schreiben von Tests in einer natürlichen Sprachsyntax.
Hier ist ein Beispiel für die Verwendung von Karate:
Feature: Testing a REST API
Scenario: Retrieve a user by ID
Given url 'https://api.example.com'
When path '/users/123'
Then status 200
And match response == { id: 123, name: 'John Doe' }
In diesem Beispiel testen wir das Abrufen eines Benutzers von einer REST-API anhand der leicht verständlichen Gehrkin-Syntax.
Gatling: Load Testing für Skalierbarkeit
Gatling ist ein leistungsstarkes Framework für die Durchführung von Load Testing. Es eignet sich hervorragend, um die Skalierbarkeit Ihrer Anwendungen zu überprüfen.
Hier ist ein Beispiel für Gatling-Testcode:
import io.gatling.core.Predef._
import io.gatling.http.Predef._
class LoadTest extends Simulation {
val httpProtocol = http.baseUrl("https://example.com")
val scn = scenario("Load Test")
.exec(http("request_1")
.get("/api/resource"))
setUp(scn.inject(atOnceUsers(10))).protocols(httpProtocol)
}
In diesem Beispiel führen wir einen Gatling-Test aus, um die Anfrageleistung für eine API-Ressource bei 10 gleichzeitigen User-Zugriffen zu überprüfen.
Testautomatisierung und Integration
Die Integration dieser Frameworks in den Entwicklungsprozess ermöglicht eine effiziente Testautomatisierung. So kann man Test-Suiten erstellen, die automatisch ausgeführt werden, wenn Codeänderungen vorgenommen werden, um sicherzustellen, dass die Anwendung weiterhin einwandfrei funktioniert.
// Kotest Test-Suite
class AllTests : StringSpec({
include(CalculatorSpec)
})
// JUnit Test-Suite
@RunWith(JUnitPlatform.class)
@SelectPackages("com.example")
public class AllTests {}
// Karate Test-Suite
class AllTestsRunner {
public static void main(String[] args) {
Results results = Runner.path("classpath:my/api/tests").tags("~@ignore").parallel(5);
generateReport(results.getReportDir());
}
// Gatling Test-Suite
class LoadTestRunner {
public static void main(String[] args) {
GatlingSimulation.runGatling()
}
}
Tests sind nur so gut wie Ihre Developer:innen
Die Verwendung von Kotest, JUnit, Karate und Gatling im Software Development ermöglicht es, die Qualität und Leistung einer Anwendungen effektiv zu überwachen und sicherzustellen. Unit-Tests, API/E2E-Tests und Load Tests werden abgedeckt und bieten somit eine hohe Testabdeckung. Die Testautomatisierung mit diesen Frameworks erleichtert die Integration von Tests in Ihren Entwicklungsprozess und trägt zur kontinuierlichen Qualitätssicherung bei.
In diesem Artikel haben wir einen Einblick in die Verwendung von Kotest, JUnit, Karate und Gatling für das Software Testing gegeben und Beispiele für alle vier Frameworks bereitgestellt. Mit diesen leistungsstarken Werkzeugen kann man sicherstellen, dass eine Anwendungen zuverlässig, fehlerfrei und skalierbar ist.
Auf jeden Fall ist es sinnvoll im Vorfeld sich auch Gedanken zu der umliegenden Architektur zum machen, bevor eine Application live gestellt wird.
Zum Thema Software Architecture haben wir uns hier schon gedanken gemacht.
Eine hohe Softwarequalität ist uns bei enableYou wichtig und darum praktizieren wir auch sorgfältige Überprüfung unseres Codes. Nur so können potentielle Fehlerquellen frühzeitig erkannt werden und auch mögliche produktive Ausfälle umgangen werden. Gleichzeitig bedarf es auch bei guten Tests einer hohen qualitativen Güte. Diese schreiben zu können verlangt ein umfassendes und tiefes Verständnis des zu testenden Systems / der zu testenden Application.
Ein hier nicht beleuchteter Aspekt ist die Bewertung der Notwenigkeit eines Tests. Hier kann der Testaufand erheblich reduziert werden, wenn das Vertrauen in eine validierte Umgebung da ist. Bspw.:
- Muss überprüft werden ob Java oder eine JVM korrekt addieren kann?
- Muss man ein im Unit Test getestetes Enum auch nochmal im E2E-Test validieren, wenn es einen fachlich oder operativ sehr wichtigen Inhalt kommuniziert?
Am Ende stellt diese Praxis nicht nur den (End-)Kund-/Benutzer:innen zufrieden sondern kann auch wirtschaftlichen Schaden abwenden.