You are currently seeing the German version of our site. Do you want to switch to the English version?

Switch to English.

Java KonferenzJavaApache MavenClean Code

Testcontainer Tests in Lichtgeschwindigkeit ausführen mit dem XDEV TCI Framework

Wir freuen uns ein neues Open Source Projekt vorstellen zu können: Testcontainers Infrastructure (TCI) Framework

Dieses Framework basiert auf Testcontainers und stellt folgende Kernfunktionen bereit:

1. Verbesserte Anpassbarkeit und Parallelisierung

Durch die Verwendung des Factory Patterns bei der Erstellung von Container wird es für den Entwickler einfacher die Container nach Bedarf anzupassen:

Ohne TCIMit TCI
static final MySQLContainer MY_SQL_CONTAINER;
static {
  MY_SQL_CONTAINER = new MySQLContainer();
  MY_SQL_CONTAINER.start();
}
static final DBTCIFactory DB_INFRA_FACTORY = new DBTCIFactory();
void startInfra() {
  this.dbInfra = DB_INFRA_FACTORY.getNew(...);
}

Für jede “Infrastruktur” (Abgekürzt: TCI) gibt es eine Factory um eine neue TCI zu erstellen. Diese Factory kann leicht konfiguriert werden und kümmert sich um Dinge wie die Erstellung von Containern, PreStarting und die Verfolgung der gestarteten TCI für die weiter unten genannten Features.

Container/Infrastruktur kann wie folgt in einer TCI_FACTORY-Klasse angepasst werden:

@Override
public void start(final String containerName) {
  super.start(containerName);
  if(doMigrate) {
    this.migrateDatabase(BASELINE_FOR_TESTS);
  }
}
void migrateDatabase(String version) {
  // Datenbank mithilfe von z.B. Flyway migrieren
}

Dies bedeutet, dass es nun möglich ist, zusätzlichen, nicht containerbezogenen Code hinzuzufügen, wie z.B. Clients oder allgemeine Methoden (z.B. createUser), ohne den Container selbst zu verändern. Dies folgt dem composition over inheritance Designprinizip.

Durch den Einsatz von Factories kann das Framework auch die Leistung mittels Parallelisierung und PreStarting verbessern.

2. Test so schnell wie möglich ausführen

Warum ist das überhaupt wichtig?

Die schnellstmögliche Ausführung von Tests hat mehrere Vorteile:

  • Bei Lokeler Ausführung:
    In der Regel gibt es nichts anderes zu tun, wenn man Tests lokal durchführt - außer vielleicht einen Kaffee zu trinken. Es ist auch möglich, eine andere Aufgabe zu beginnen, aber dann verliert man vielleicht den Fokus auf die ursprüngliche Aufgabe und muss sich später wieder in das Thema einarbeiten.
     
  • Bei Benutzung einer CI:
    • Wenn man für Rechenleistung bei Bedarf bezahlt (z. B. minutenbasierte Abrechnung von Spot-Instances) kann die schnellere Durchführung von Tests (ohne Vergrößerung des verwendeten Rechners) aufgrund der geringeren Mietdauer viel Geld sparen.
       
    • Wenn man für eine feste Menge an Rechenleistung bezahlt, bedeutet eine schnellere Ausführung von Tests, dass mehr Zeit für andere Aufgaben zur Verfügung steht, die auf der CI ausgeführt werden können. 
      Wenn die Zeitersparnis groß genug ist, kann man auch über eine Verringerung der erforderlichen Rechenleistung nachdenken.
       
    • Schnelleres Test-Feedback: Wenn z.B. vor einem Release alle Integrationstests erfolgreich ausgeführt werden müssen, kann dies die Zeit für die Auslieferung des Releases verkürzen.

Das Framework ist explizit auf Parallelisierung ausgelegt und bietet mehrere Funktionen zur Beschleunigung von Tests:

2.1. PreStarting Mechanismus

Bei der Durchführung von Tests gibt es in der Regel bestimmte Zeiten, in denen die verfügbaren Ressourcen kaum ausgelastet sind:

PreStarting verwendet einen gecachten Pool von Infrastrukturen und versucht, diese Leerlaufzeiten zu nutzen, um diesen Pool aufzufüllen. Wenn eine neue Infrastruktur angefordert wird, muss nicht auf ihre Erstellung gewartet werden, sondern es kann die bereits gestartete Infrastruktur aus diesem Pool verwendet werden - sofern sie verfügbar ist.

Performance-Boost

Wenn PreStarting korrekt implementiert wurde kann dies einen enormen Leistungsunterschied bewirken, wie in unserem Leistungsvergleich zu sehen ist

Des Weiteren gibt es auch ein Live-Beispiel (mit GitHub Actions), das die folgenden Ergebnisse liefert:

FallParallelisierungPreStarting aktiv?Benötigte Zeit um alle Tests auszuführen
A-8m 50s
B-5m 30s
C26m
D24m 50s

Wie wir sehen können, gibt es im besten Fall (D) eine Geschwindigkeitsverbesserung von fast 50 % im Vergleich zur Durchführung der Tests auf herkömmliche Weise (A).

2.2. Optimierte Testcontainers Netzwerke

Es wird eine optimierte Implementierung von Testcontainers Network verwendet:

VorherNachher

NetworkImpl code aus Testcontainers 1.20

@Override
public synchronized String getId() {
  if (initialized.compareAndSet(false, true)) {
    boolean success = false;
    try {
      // Netzwerk wird erstellt wenn auf die ID zugegriffen wird
      // Dauert einen Moment
      id = create();
      success = true;
    } finally {
      ...
    }
  }
  return id;
}
LazyNetworkPool bietet einen Pool von Netzwerken, die im Hintergrund erstellt werden.
Es geht keine Zeit mit dem Warten auf die Netzwerkerstellung verloren.

2.3. Container Leck Erkennung

Erkennt ob gestartete Container/Infrastruktur auch beendet wurden und verhindert dadurch, dass es zur Ressourcenerschöpfung kommt.
Im folgenden Beispiel wird der Testcontainer zwar erstellt, aber nie beendet.

@Test
void test() {
  DummyTCI tci = DUMMY_FACTORY.getNew(...);
  ...
}

Nachdem die Tests mit dem Framework ausgeführt wurden, erscheint folgende Fehlermeldung:

ERROR s.x.tci.leakdetection.TCILeakAgent - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
ERROR s.x.tci.leakdetection.TCILeakAgent - ! PANIC: DETECTED CONTAINER INFRASTRUCTURE LEAK !
ERROR s.x.tci.leakdetection.TCILeakAgent - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
ERROR s.x.tci.leakdetection.TCILeakAgent - All test are finished but some infrastructure is still marked as in use:
DummyTCIFactory leaked 1x [container-ids=[c1b6be852fac3bf65ac8f2739ab161d7f95bc4c62699c698ccc8b74da1be8a3d]]

3. Quality of Life Verbesserungen

Das Framework enthält auch einige kleinere Verbesserungen:

3.1. Container-Namen die ein Mensch lesen kann

Alle gestarteten Container haben einen eindeutigen, von Menschen lesbaren Namen. was die Identifizierung erleichtert.

VorherNachher
docker stats
 
NAME 
eager_rubin 
vigilant_archimedes 
practical_haibt 
ecstatic_sanderson 
serene_einstein 
great_saha 
agitated_dhawan 
strange_montalcini
docker stats
NAME 
selenium-chrome-2-PS-1-... 
selenium-firefox-1-PS-1-... 
selenium-chrome-1-PS-1-... 
db-mariadb-1-PS-1-... 
oidc-2-PS-1-... 
selenium-firefox-2-PS-1-... 
recorder-selenium-chrome-1-PS-1-... 
recorder-selenium-firefox-1-PS-1-...

3.2. Statistiken zur Testlaufzeit

Ein Nachverfolgungsmechanismus, der das Auffinden von Engpässen und ähnlichen Problemen erleichtert
Beispiel:

[main] [i.tracing.TCITracingAgent] === Test Tracing Info ===
Duration: 2m 43.608s
Tests: 20.656s / 15 / 5m 9.84s
BrowserTCIFactory-firefox:
  bootNew - 1ms / 6 / 5ms
  connectToNetwork - 515ms / 5 / 2.575s
  getNew - 574ms / 5 / 2.87s
  infraStart(async) - 14.575s / 6 / 1m 27.448s
  postProcessNew - 54ms / 5 / 270ms
  warmUp - 2.448s / 1 / 2.448s
...

Weitere Informationen

Das Framework ist auf GitHub verfügbar. Details zu Benutzung findet man unter “Usage”. Es sind auch mehrere Demos verfügbar.

Bei weitere Fragen oder falls Hilfe benötigt wird, kann man ein Issue erstellen oder unseren Support kontaktieren.

Und nicht vergessen: Gerne kann man dem Repo auch einen Stern 🌟 geben.

Kontakt