GZoltar / gzoltar

GZoltar - Java Library for Automatic Debugging http://www.gzoltar.com
Other
77 stars 34 forks source link

Investigate whether our module to find and run tests can be replaced with the new JUnit Platform #25

Open jose opened 4 years ago

jose commented 4 years ago

It seems that the latest JUnit version, JUnit 5, includes a module 'JUnit Platform' which:

is responsible for running and discovering tests. This API is essential for third-party tools (e.g. IntelliJ Idea or NetBeans), as well as build tools (e.g. Maven or Gradle). It provides the TestEngine API, which is responsible for executing tests. The normal application developer, who is writing tests for a certain application, does not need to deal directly with the platform API.

Here are two examples on how to use the JUnit Platform to find and run tests.

Example found on stackoverflow

import static org.junit.platform.engine.discovery.ClassNameFilter.includeClassNamePatterns;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;

import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.TestExecutionListener;
import org.junit.platform.launcher.TestIdentifier;
import org.junit.platform.launcher.TestPlan;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.listeners.LoggingListener;

public class MainPrueba {

    public static void main(String[] args) throws InterruptedException {

        Runnable task = () -> {
            System.out.println("Runing thread INI");

            LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
                .selectors(
                        selectPackage("org.package.qabootfx.test.ping")
                        //,selectClass(QabootfxApplicationTests.class)
                )
                .filters(
                    //includeClassNamePatterns(".*Test")
                        includeClassNamePatterns(".*")
                )
                .build();

            Launcher launcher = LauncherFactory.create();

            TestPlan testPlan = launcher.discover(request);

            for (TestIdentifier root : testPlan.getRoots()) {
                System.out.println("Root: " + root.toString());

                for (TestIdentifier test : testPlan.getChildren(root)) {
                    System.out.println("Found test: " + test.toString());
                }
            }

            // Register a listener of your choice
            //TestExecutionListener listener = new SummaryGeneratingListener();
            TestExecutionListener listener = LoggingListener.forJavaUtilLogging(); //new LoggingListener();
            launcher.registerTestExecutionListeners(listener);

            launcher.execute(request);
            System.out.println("Running thread END");
        };
        new Thread(task).start();

        Thread.sleep(5000);
        System.out.println("END");
    }
}

Official example

/*
 * Copyright 2015-2020 the original author or authors.
 *
 * All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License v2.0 which
 * accompanies this distribution and is available at
 *
 * https://www.eclipse.org/legal/epl-v20.html
 */

package example;

// tag::imports[]
import static org.junit.platform.engine.discovery.ClassNameFilter.includeClassNamePatterns;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;

import java.io.PrintWriter;
import java.nio.file.Path;
import java.nio.file.Paths;

import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.TestExecutionListener;
import org.junit.platform.launcher.TestPlan;
import org.junit.platform.launcher.core.LauncherConfig;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
import org.junit.platform.launcher.listeners.TestExecutionSummary;
import org.junit.platform.reporting.legacy.xml.LegacyXmlReportGeneratingListener;
// end::imports[]

/**
 * @since 5.0
 */
class UsingTheLauncherDemo {

    @org.junit.jupiter.api.Test
    @SuppressWarnings("unused")
    void discovery() {
        // @formatter:off
        // tag::discovery[]
        LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
            .selectors(
                selectPackage("com.example.mytests"),
                selectClass(MyTestClass.class)
            )
            .filters(
                includeClassNamePatterns(".*Tests")
            )
            .build();

        Launcher launcher = LauncherFactory.create();

        TestPlan testPlan = launcher.discover(request);
        // end::discovery[]
        // @formatter:on
    }

    @org.junit.jupiter.api.Test
    @SuppressWarnings("unused")
    void execution() {
        // @formatter:off
        // tag::execution[]
        LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
            .selectors(
                selectPackage("com.example.mytests"),
                selectClass(MyTestClass.class)
            )
            .filters(
                includeClassNamePatterns(".*Tests")
            )
            .build();

        Launcher launcher = LauncherFactory.create();

        // Register a listener of your choice
        SummaryGeneratingListener listener = new SummaryGeneratingListener();
        launcher.registerTestExecutionListeners(listener);

        launcher.execute(request);

        TestExecutionSummary summary = listener.getSummary();
        // Do something with the TestExecutionSummary.

        // end::execution[]
        // @formatter:on
    }

    @org.junit.jupiter.api.Test
    void launcherConfig() {
        Path reportsDir = Paths.get("target", "xml-reports");
        PrintWriter out = new PrintWriter(System.out);
        // @formatter:off
        // tag::launcherConfig[]
        LauncherConfig launcherConfig = LauncherConfig.builder()
            .enableTestEngineAutoRegistration(false)
            .enableTestExecutionListenerAutoRegistration(false)
            .addTestEngines(new CustomTestEngine())
            .addTestExecutionListeners(new LegacyXmlReportGeneratingListener(reportsDir, out))
            .addTestExecutionListeners(new CustomTestExecutionListener())
            .build();

        Launcher launcher = LauncherFactory.create(launcherConfig);

        LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
            .selectors(selectPackage("com.example.mytests"))
            .build();

        launcher.execute(request);
        // end::launcherConfig[]
        // @formatter:on
    }

}

class MyTestClass {
}

class CustomTestExecutionListener implements TestExecutionListener {
}

Can replace our module to find and run tests with this 'platform'? What are the advantages and disadvantages? Does it allow to run individual unit test cases rather than test classes? This last question is quite important to GZoltar, as it must collect coverage per unit test, and according to JUnit 5 documentation it might be possible to achieve that out-of-the-box.