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);
    }
}