Was ist beim Umstieg zu beachten? Was ändert sich?
Gradle
dependencies {
testCompile 'org.junit.platform:junit-platform-commons:1.4.0'
testCompile(
'org.junit.jupiter:junit-jupiter-api:5.4.0',
'org.junit.jupiter:junit-jupiter-params:5.4.0'
)
testRuntime(
'org.junit.jupiter:junit-jupiter-engine:5.4.0',
'org.junit.vintage:junit-vintage-engine:5.4.0'
)
}
Man kann JUnit 4 und JUnit 5 parallel im Projekt verwenden: junit-jupiter ist JUnit 5 und junit-vintage ist JUnit 4.
Annotation
Gegenüberstellung der Annotationen: JUnit 4 vs. JUnit 5
- @Test -> @Test
- @Before -> @BeforeEach
- @After -> @AfterEach
- @BeforeClass -> @BeforeAll
- @AfterClass -> @AfterAll
- @Ignore -> @Disabled
Imports in der Testklasse
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
Parametrisierte Tests
In JUnit 4 mussten parametrisierte Tests in eigenen Klassen stehen. Jetzt können diese mit nicht parametrisierten Tests kombiniert werden.
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.NullAndEmptySource;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
@ParameterizedTest
@MethodSource("daten")
void testMethode(String arg1, Integer arg2) {
..
}
private static Stream<Arguments> daten() {
return Stream.of(
Arguments.of("blub", 1),
Arguments.of("tada", 17)
);
}
@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = {org.apache.commons.lang3.StringUtils.SPACE})
public void sollteBeiNullwertenEineExceptionWerfen(String arg) {
..
}
Exceptions
Wenn man im Test eine Exception erwartet, konnte man in JUnit 4 dies in der Annotation angeben:
@Test(expected = Exception.class)
In JUnit 5 macht man dies in der Methode mit:
Assertions.assertThrows(Exception.class, () -> {..});
Testklassen zählen
Wenn man in einem Projekt von JUnit 4 auf JUnit 5 umsteigt, macht es Sinn, hin und wieder zu schauen, wie weit man damit schon fortgeschritten ist. Dafür könnte man die Testklassen zählen. Die folgende Datei unter test/ erstellen und als Anwendung starten. Geht bestimmt auch schöner, reicht für informative Zwecke aber aus.
package packagename.util;
import static packagename.util.JunitCounter.Testclass.JUNIT_4;
import static packagename.util.JunitCounter.Testclass.JUNIT_5;
import static packagename.util.JunitCounter.Testclass.NONE;
import static java.lang.System.lineSeparator;
import static java.util.Optional.ofNullable;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.apache.commons.lang3.StringUtils.SPACE;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
/**
* Zählt die Anzahl der jUnit4 und jUnit5 Testklassen.
*/
public class JunitCounter {
private static final String IMPORT_JUNIT_4 = "import org.junit.Test;";
private static final String IMPORT_JUNIT_5 = "import org.junit.jupiter.api.Test;";
private static final String IMPORT_JUNIT_5_PARAMETERIZED = "import org.junit.jupiter.params.ParameterizedTest;";
enum Testclass {NONE, JUNIT_4, JUNIT_5}
public static void main(String[] args) {
JunitCounter junitCounter = new JunitCounter();
junitCounter.countTestclasses();
}
private void countTestclasses() {
int counterJunit4 = 0;
int counterJunit5 = 0;
int counterNone = 0;
List<String> junit4Classes = new ArrayList<>();
List<File> javaFiles = allFilesFrom(getTestDir());
for (File currentFile : javaFiles) {
Testclass type = testclassTypeFrom(currentFile);
switch (type) {
case JUNIT_4:
counterJunit4++;
junit4Classes.add(currentFile.getName());
break;
case JUNIT_5:
counterJunit5++;
break;
case NONE:
counterNone++;
break;
default:
break;
}
}
System.out.println("Found: " + javaFiles.size() + " files " + lineSeparator() + " jUnit4: " + counterJunit4 + " classes ("
+ formatPercentage(counterJunit4, counterJunit4 + counterJunit5) + ")" + lineSeparator() + " jUnit5: " + counterJunit5 + " classes ("
+ formatPercentage(counterJunit5, counterJunit4 + counterJunit5) + ")"
+ lineSeparator() + SPACE + counterNone + " classes without tests"
+ lineSeparator() + lineSeparator() + StringUtils.repeat("-", 50) + lineSeparator() + "Junit4Classes: " + lineSeparator());
junit4Classes.stream().sorted().forEach(System.out::println);
}
private String formatPercentage(int part, int sum) {
return String.format("%,.2f", part * 100.0f / sum) + " %";
}
private List<File> allFilesFrom(File directory) {
List<File> allFiles = new ArrayList<>();
for (File currentFile : ofNullable(directory.listFiles()).orElse(new File[0])) {
if (currentFile.isDirectory()) {
allFiles.addAll(allFilesFrom(currentFile));
} else {
allFiles.add(currentFile);
}
}
return allFiles;
}
private Testclass testclassTypeFrom(File file) {
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line;
while ((line = reader.readLine()) != null) {
if (line.trim().startsWith(IMPORT_JUNIT_4)) {
return JUNIT_4;
} else if (line.trim().startsWith(IMPORT_JUNIT_5) || line.trim().startsWith(IMPORT_JUNIT_5_PARAMETERIZED)) {
return JUNIT_5;
}
}
} catch (IOException e) {
e.printStackTrace();
}
return NONE;
}
private File getTestDir() {
String scsName = "projectname/";
String filename = this.getClass().getClassLoader().getResource(EMPTY).getFile();
String dirName = filename.substring(0, filename.indexOf(scsName) + scsName.length()) + "src/test/java";
System.out.println("Searching for test classes in " + dirName + " ...");
return new File(dirName);
}
}