From 859527ac58f790f8ebbf567f52a0fc0a64fd3141 Mon Sep 17 00:00:00 2001 From: Pavel Jandejsek Date: Mon, 18 Dec 2023 20:57:14 +0100 Subject: [PATCH] Add support for ordering of stories in the JUnit5 engine. --- README.md | 22 +++++ .../engine/JBehaveTestEngine.java | 25 ++++- .../jbehavesupport/engine/OrderingTest.groovy | 91 +++++++++++++++++++ 3 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 src/test/groovy/org/jbehavesupport/engine/OrderingTest.groovy diff --git a/README.md b/README.md index 458e762..53fbe94 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,28 @@ public class BasicStory extends JUnit5Story { } ``` +#### Ordering of test classes +The ordering of classes is not guaranteed by default by the engine. +If you need to influence order you can supply your own comparator class name in the parameter `jbehave.execution.order.comparator` +either in system properties or in the JUnit Platform configuration file named `junit-platform.properties`. +This comparator needs to implement the `Comparator` interface and have a no-args constructor available. + +Example configuration: +```java +package com.application.comparator; + +public class DisplayNameComparator implements Comparator { + @Override + public int compare(TestDescriptor o1, TestDescriptor o2) { + return o1.getDisplayName().compareTo(o2.getDisplayName()); + } +} +``` +junit-platform.properties: +```properties +jbehave.execution.order.comparator=com.application.comparator.DisplayNameComparator +``` + ### JUnit 4 To use JUnit4 runner please add a dependency for `junit` or `junit-vintage-engine` to your project explicitly. Very simple java class with runner implementation: diff --git a/src/main/java/org/jbehavesupport/engine/JBehaveTestEngine.java b/src/main/java/org/jbehavesupport/engine/JBehaveTestEngine.java index 451c14f..e808493 100644 --- a/src/main/java/org/jbehavesupport/engine/JBehaveTestEngine.java +++ b/src/main/java/org/jbehavesupport/engine/JBehaveTestEngine.java @@ -18,6 +18,7 @@ */ package org.jbehavesupport.engine; +import lombok.SneakyThrows; import org.jbehavesupport.engine.descriptor.JBehaveTestDescriptor; import org.jbehavesupport.engine.discovery.JBehaveDiscoverer; import org.jbehavesupport.engine.executor.JBehaveExecutor; @@ -28,13 +29,17 @@ import org.junit.platform.engine.TestEngine; import org.junit.platform.engine.UniqueId; +import java.util.Comparator; import java.util.Optional; +import java.util.stream.Stream; import static org.junit.platform.engine.TestExecutionResult.successful; public final class JBehaveTestEngine implements TestEngine { - @Override + public static final String COMPARATOR_PROPERTY = "jbehave.execution.order.comparator"; + + @Override public String getId() { return "jbehave"; } @@ -56,17 +61,29 @@ public TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId @Override public void execute(ExecutionRequest request) { + Optional> sortingComparator = request.getConfigurationParameters() + .get(COMPARATOR_PROPERTY, JBehaveTestEngine::getComparatorInstance); + EngineExecutionListener engineExecutionListener = request.getEngineExecutionListener(); TestDescriptor engineDescriptor = request.getRootTestDescriptor(); engineExecutionListener.executionStarted(engineDescriptor); JBehaveExecutor jBehaveExecutor = new JBehaveExecutor(request); - engineDescriptor.getChildren() + Stream testDescriptorStream = engineDescriptor.getChildren() .stream() .map(JBehaveTestDescriptor.class::cast) - .filter(JBehaveTestDescriptor::isRunnable) - .forEach(jBehaveExecutor::execute); + .filter(JBehaveTestDescriptor::isRunnable); + + if (sortingComparator.isPresent()) { + testDescriptorStream = testDescriptorStream.sorted(sortingComparator.get()); + } + testDescriptorStream.forEach(jBehaveExecutor::execute); engineExecutionListener.executionFinished(engineDescriptor, successful()); } + @SneakyThrows(ReflectiveOperationException.class) + private static Comparator getComparatorInstance(String className) { + return (Comparator) Class.forName(className).newInstance(); + } + } diff --git a/src/test/groovy/org/jbehavesupport/engine/OrderingTest.groovy b/src/test/groovy/org/jbehavesupport/engine/OrderingTest.groovy new file mode 100644 index 0000000..a758f99 --- /dev/null +++ b/src/test/groovy/org/jbehavesupport/engine/OrderingTest.groovy @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jbehavesupport.engine + +import org.jbehavesupport.engine.story.AndStepStories +import org.jbehavesupport.engine.story.BasicStory +import org.junit.platform.engine.TestDescriptor +import org.junit.platform.testkit.engine.EngineTestKit +import spock.lang.Specification +import spock.lang.Unroll +import spock.util.environment.RestoreSystemProperties + +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass +import static org.junit.platform.testkit.engine.EventConditions.container +import static org.junit.platform.testkit.engine.EventConditions.engine +import static org.junit.platform.testkit.engine.EventConditions.event +import static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully +import static org.junit.platform.testkit.engine.EventConditions.started +import static org.junit.platform.testkit.engine.EventConditions.test + +class OrderingTest extends Specification { + + @Unroll + @RestoreSystemProperties + def "Test ordering with #comparatorClassName"() { + given: + System.setProperty("jbehave.report.level", "STORY") + System.setProperty(JBehaveTestEngine.COMPARATOR_PROPERTY, comparatorClassName) + EngineTestKit.Builder builder = EngineTestKit.engine("jbehave") + .enableImplicitConfigurationParameters(true) + .selectors(selectClass(BasicStory), selectClass(AndStepStories)) + + when: + def executionResults = builder.execute() + + then: + executionResults.allEvents() + .assertEventsMatchExactly( + event(engine(), started()), + event(container(firstClass), started()), + event(test(firstStory), started()), + event(test(firstStory), finishedSuccessfully()), + event(container(firstClass), finishedSuccessfully()), + event(container(secondClass), started()), + event(test(secondStory), started()), + event(test(secondStory), finishedSuccessfully()), + event(container(secondClass), finishedSuccessfully()), + event(engine(), finishedSuccessfully()) + ) + + where: + comparatorClassName || firstStory || firstClass || secondStory || secondClass + ReverseComparator.class.getName() || "basic_story" || BasicStory || "AndStep" || AndStepStories + DisplayNameComparator.class.getName() || "AndStep" || AndStepStories || "basic_story" || BasicStory + + } + +} + +class ReverseComparator implements Comparator { + + private DisplayNameComparator displayNameComparator = new DisplayNameComparator() + + @Override + int compare(TestDescriptor o1, TestDescriptor o2) { + displayNameComparator.reversed().compare(o1, o2) + } +} + +class DisplayNameComparator implements Comparator { + @Override + int compare(TestDescriptor o1, TestDescriptor o2) { + o1.displayName <=> o2.displayName + } +}