2021.06
2025.02
Modultests mit JUnit
MyCoRe bietet dedizierte Hilfsklassen und JUnit-Erweiterungen, die das Erstellen von Unit- und Integrationstests vereinfachen. Diese Utilities verwalten die Komplexität der MyCoRe-Umgebung (Konfiguration, Datenbank, Storage, Sessions), sodass sich Entwickler auf die Testlogik konzentrieren können.
Einleitung
MyCoRe stellt Utilities bereit, um das Erstellen von Unit- und Integrationstests mit dem JUnit-Framework zu vereinfachen. Diese Helfer übernehmen das Management der MyCoRe-Umgebung (Konfiguration, Datenbank, Storage, Sessions) und ermöglichen es Entwicklern, sich auf das Testen der Komponentenlogik zu konzentrieren. Dieses Dokument beschreibt den modernen Ansatz mit JUnit 5 (empfohlen für neue Tests) sowie die Unterstützung für das ältere JUnit 4.
JUnit 5 Support (Empfohlen)
Seit MyCoRe
2025.02
wird JUnit 5 mit dedizierten Erweiterungen unterstützt, die das Testen von MyCoRe-Komponenten vereinfachen. Das modulare Extension-Modell von JUnit 5 wird hierbei genutzt, um spezifische Aspekte der Testumgebung gezielt zu aktivieren. Tipps zur Migration von JUnit 4 auf JUnit 5 finden Sie hier im offiziellen JUnit 5 User Guide.
Basis-Setup mit @MyCoReTest
Die Annotation @MyCoReTest
ist die grundlegende Annotation für JUnit 5 Tests in MyCoRe. Sie muss auf Testklassen angewendet werden, die eine MyCoRe-Laufzeitumgebung benötigen.
Ihre Hauptaufgaben umfassen:
- Initialisierung der Basis-MyCoRe-Umgebung.
- Einrichtung eines temporären Verzeichnisses für
MCR.Home
.
- Laden der Standard-MyCoRe-Konfigurationsproperties (
mycore.properties
).
- Verwaltung des
MCRSession
-Lebenszyklus für jede Testmethode (Session wird vor dem Test verfügbar gemacht und danach geschlossen).
- Ermöglicht das Setzen von test-spezifischen Konfigurationsproperties über
@MCRTestConfiguration
und @MCRTestProperty
(siehe Beispiel).
Mit @MCRTestConfiguration
können Properties auf Klassen- oder Methodenebene definiert oder überschrieben werden.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mycore.common.MCRTestConfiguration;
import org.mycore.common.MCRTestProperty;
import org.mycore.common.config.MCRConfiguration2;
import org.mycore.common.config.MCRConfigurationBase;
import org.mycore.common.config.MCRConfigurationException;
import org.mycore.test.MyCoReTest;
// Basis-Annotation für MyCoRe Tests
@MyCoReTest
// Test-spezifische Properties auf Klassenebene
@MCRTestConfiguration(
properties = {
// Property mit Klassennamen als Wert
@MCRTestProperty(key = "MCR.Test.Class", classNameOf = MCRMyUnitTest.class),
// Property mit String-Wert
@MCRTestProperty(key = "MCR.myProperty", string = "junit-class-level")
}
)
public class MCRMyUnitTest {
@Test
public void testClassProperty() {
// Zugriff auf Property, das auf Klassenebene definiert wurde
Assertions.assertEquals(
"junit-class-level",
MCRConfiguration2.getStringOrThrow("MCR.myProperty")
);
Assertions.assertEquals(
MCRMyUnitTest.class.getName(),
MCRConfiguration2.getStringOrThrow("MCR.Test.Class")
);
}
// Zusätzliche / überschreibende Properties für diese Methode
@Test
@MCRTestConfiguration(
properties = {
// Überschreibt den Wert von "MCR.myProperty" nur für diesen Test
@MCRTestProperty(key = "MCR.myProperty", string = "junit-method-level"),
// Definiert ein leeres Property
@MCRTestProperty(key = "MCR.emptyProperty", empty = true)
}
)
public void testMethodProperty() {
// Property von Methode überschreibt Property von Klasse
Assertions.assertEquals(
"junit-method-level",
MCRConfiguration2.getStringOrThrow("MCR.myProperty")
);
Assertions.assertTrue(MCRConfigurationBase.getString("MCR.emptyProperty").isPresent());
Assertions.assertTrue(MCRConfigurationBase.getString("MCR.emptyProperty").get().isEmpty());
Assertions.assertThrowsExactly(MCRConfigurationException.class,
() -> MCRConfiguration2.getStringOrThrow("MCR.emptyProperty"));
}
}
|
Datenbank-Setup mit MCRJPAExtension
Wenn ein Test Datenbankzugriffe über JPA erfordert, aktivieren Sie zusätzlich zu @MyCoReTest
die MCRJPAExtension
mit @ExtendWith(MCRJPAExtension.class)
. Diese Erweiterung kümmert sich spezifisch um:
- Einrichtung einer dedizierten In-Memory-Datenbank (H2), isoliert pro Testklasse bzw. Komponente.
- Automatische Generierung des Datenbankschemas (
create
) basierend auf den JPA-Entitäten vor den Tests der Klasse.
- Automatisches Löschen des Schemas (
drop
) nach allen Tests der Klasse.
- Transaktionsmanagement: Startet eine Transaktion vor jeder Testmethode und führt am Ende einen Commit oder Rollback durch.
- Datenbankbereinigung: Löscht die Inhalte aller Tabellen (
TRUNCATE
) nach jeder Testmethode, um Testisolation sicherzustellen.
- Stellt sicher, dass ein
EntityManager
über MCREntityManagerProvider.getCurrentEntityManager()
verfügbar ist.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mycore.backend.jpa.MCREntityManagerProvider;
import org.mycore.test.MCRJPAExtension; // Import der Extension
import org.mycore.test.MyCoReTest;
@MyCoReTest // Basis-Setup
@ExtendWith(MCRJPAExtension.class) // Zusätzliches JPA Setup aktivieren
public class MCRJPAExtensionTest {
@Test
public void testEntityManagerAvailable() {
Assertions.assertNotNull(
MCREntityManagerProvider.getCurrentEntityManager(),
"EntityManager should be available"
);
// Hier können nun JPA Operationen durchgeführt werden
}
// Funktioniert auch in verschachtelten Tests
@Nested
class MCRJPAExtensionNestedTest {
@Test
public void testEntityManagerAvailableInNested() {
Assertions.assertNotNull(
MCREntityManagerProvider.getCurrentEntityManager(),
"EntityManager should be available in nested test"
);
}
}
}
|
Metadaten-Storage-Setup mit MCRMetadataExtension
Für Tests, die MyCoRe-Objekte über den MCRXMLMetadataManager
speichern oder laden (z.B. Interaktion mit dem Dateisystem- oder SVN-basierten Storage), verwenden Sie zusätzlich zu @MyCoReTest
die MCRMetadataExtension
mit @ExtendWith(MCRMetadataExtension.class)
. Sie ist verantwortlich für:
- Einrichtung von temporären Verzeichnissen für den Metadaten-Store (
MCR.Metadata.Store.BaseDir
) und optional für SVN (MCR.Metadata.Store.SVNBase
).
- Konfiguration der entsprechenden MyCoRe-Properties, um diese temporären Verzeichnisse zu nutzen.
- Bereinigung dieser Verzeichnisse nach jeder Testmethode.
- Sicherstellung, dass der
MCRXMLMetadataManager
initialisiert und einsatzbereit ist.
Hinweis: Die MCRJPAExtension
ist für die reine Nutzung der MCRMetadataExtension
nicht zwingend erforderlich, wird aber oft zusammen verwendet, wenn Tests sowohl Metadaten- als auch Datenbankoperationen umfassen (z.B. Speichern eines MCRObjects und der zugehörigen Datenbankeinträge).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mycore.common.MCRTestConfiguration;
import org.mycore.common.MCRTestProperty;
import org.mycore.common.config.MCRConfiguration2;
import org.mycore.datamodel.common.MCRXMLMetadataManager;
import org.mycore.datamodel.metadata.MCRObjectID;
import org.mycore.test.MCRJPAExtension; // Optional, wenn DB benötigt wird
import org.mycore.test.MCRMetadataExtension; // Import der Extension
import org.mycore.test.MyCoReTest;
@MyCoReTest // Basis-Setup
@ExtendWith(MCRJPAExtension.class) // Optional, nur wenn DB-Interaktion auch getestet wird
@ExtendWith(MCRMetadataExtension.class) // Metadaten-Storage Setup aktivieren
@MCRTestConfiguration(
properties = {
// Beispiel: Konfiguration für einen Metadatentyp
@MCRTestProperty(key = "MCR.Metadata.Type.test", string = "true")
})
public class MCRMetadataExtensionTest {
@Test
public void testXMLMetadataManagerAvailable() {
// Property wird von MCRMetadataExtension gesetzt
Path storeBaseDir = MCRConfiguration2.getOrThrow("MCR.Metadata.Store.BaseDir", Paths::get);
System.out.println("Store BaseDir=" + storeBaseDir.toAbsolutePath());
Assertions.assertTrue(Files.isDirectory(storeBaseDir), "Store base dir should exist");
// MCRXMLMetadataManager ist nun konfiguriert und kann verwendet werden
Assertions.assertFalse(
MCRXMLMetadataManager.getInstance().exists(MCRObjectID.getInstance("MyCoRe_test_00004711")),
"Object should not exist in the temporary store initially"
);
// Hier können nun Operationen wie store(), retrieve() etc. getestet werden
}
}
|
JUnit 4 Support (Legacy)
Für ältere Tests oder Projekte, die noch nicht auf JUnit 5 migriert wurden, bietet MyCoRe Basisklassen für JUnit 4 Tests. Es wird jedoch empfohlen, neue Tests mit JUnit 5 und den oben beschriebenen Extensions zu schreiben.
Tests sollten von der Klasse MCRTestCase
abgeleitet werden. Diese Klasse initialisiert das Basis-System und ermöglicht es, über die Methode getTestProperties()
test-spezifische Properties zu setzen.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import java.util.Map;
import org.junit.Test; // JUnit 4 Annotation
import org.mycore.common.config.MCRConfiguration2;
import org.mycore.test.MCRTestCase; // JUnit 4 Basisklasse
public class MCRMyLegacyUnitTest extends MCRTestCase { // Ableiten von MCRTestCase
@Test // JUnit 4 Test-Annotation
public void myTestMethod() {
// Property aus getTestProperties() ist verfügbar
MCRConfiguration2.getStringOrThrow("meinProperty");
}
// Überschreiben, um Test-Properties hinzuzufügen
@Override
protected Map<String, String> getTestProperties() {
Map<String, String> properties = super.getTestProperties();
properties.put("meinProperty", "wert");
return properties;
}
}
|
Mit MCRTestCase
allein wird die Datenbank nicht initialisiert. Wird Datenbankzugriff benötigt, sollte die Testklasse stattdessen von MCRJPATestCase
abgeleitet werden. Wenn Objekte über den MCRXMLMetadataManager
gespeichert oder gelesen werden sollen (Dateisystem/SVN-Storage), sollte von MCRStoreTestCase
abgeleitet werden.