diff --git a/docs/junit5.adoc b/docs/junit5.adoc index 3b50ad111e..d3b9ef78ce 100644 --- a/docs/junit5.adoc +++ b/docs/junit5.adoc @@ -74,6 +74,7 @@ public class JGiven5ScenarioTest } } ---- +Please note that JGiven is built around each scenario, i.e. test method, having its own test instance. Therefore, we cannot support JUnit's per-class test instance lifecycle. An exception will be thrown if any such attempt is made. === Example Project diff --git a/jgiven-junit5/src/main/java/com/tngtech/jgiven/junit5/JGivenExtension.java b/jgiven-junit5/src/main/java/com/tngtech/jgiven/junit5/JGivenExtension.java index 65ff5d034e..dd32c2043a 100644 --- a/jgiven-junit5/src/main/java/com/tngtech/jgiven/junit5/JGivenExtension.java +++ b/jgiven-junit5/src/main/java/com/tngtech/jgiven/junit5/JGivenExtension.java @@ -3,20 +3,25 @@ import static com.tngtech.jgiven.report.model.ExecutionStatus.FAILED; import static com.tngtech.jgiven.report.model.ExecutionStatus.SUCCESS; -import java.util.EnumSet; - -import org.junit.jupiter.api.Assumptions; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.extension.*; -import org.junit.jupiter.api.extension.ExtensionContext.Namespace; - import com.tngtech.jgiven.base.ScenarioTestBase; import com.tngtech.jgiven.config.AbstractJGivenConfiguration; import com.tngtech.jgiven.config.ConfigurationUtil; +import com.tngtech.jgiven.exception.JGivenWrongUsageException; import com.tngtech.jgiven.impl.ScenarioBase; import com.tngtech.jgiven.impl.ScenarioHolder; import com.tngtech.jgiven.report.impl.CommonReportHelper; import com.tngtech.jgiven.report.model.ReportModel; +import java.util.EnumSet; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.AfterTestExecutionCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Namespace; +import org.junit.jupiter.api.extension.TestInstancePostProcessor; /** * This extension enables JGiven for JUnit 5 Tests. @@ -39,7 +44,7 @@ public class JGivenExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, - AfterEachCallback { + AfterTestExecutionCallback { private static final Namespace NAMESPACE = Namespace.create("com.tngtech.jgiven"); @@ -47,6 +52,8 @@ public class JGivenExtension implements @Override public void beforeAll(ExtensionContext context) { + validatePerMethodLifecycle(context); + ReportModel reportModel = new ReportModel(); reportModel.setTestClass(context.getTestClass().get()); if (!context.getDisplayName().equals(context.getTestClass().get().getSimpleName())) { @@ -62,6 +69,7 @@ public void beforeAll(ExtensionContext context) { } } + @Override public void afterAll(ExtensionContext context) { ScenarioHolder.get().removeScenarioOfCurrentThread(); @@ -75,7 +83,7 @@ public void beforeEach(ExtensionContext context) { } @Override - public void afterEach(ExtensionContext context) throws Exception { + public void afterTestExecution(ExtensionContext context) throws Exception { ScenarioBase scenario = getScenario(); try { if (context.getExecutionException().isPresent()) { @@ -96,10 +104,6 @@ public void afterEach(ExtensionContext context) throws Exception { } } - private ScenarioBase getScenario() { - return ScenarioHolder.get().getScenarioOfCurrentThread(); - } - @Override public void postProcessTestInstance(Object testInstance, ExtensionContext context) { ScenarioBase currentScenario = ScenarioHolder.get().getScenarioOfCurrentThread(); @@ -125,4 +129,21 @@ public void postProcessTestInstance(Object testInstance, ExtensionContext contex scenario.getExecutor().readScenarioState(testInstance); } + private void validatePerMethodLifecycle(ExtensionContext context) { + if (isPerClassLifecycle(context)) { + throw new JGivenWrongUsageException( + "JGiven does not support keeping a test instance over multiple scenarios. Please use Lifecycle '" + + TestInstance.Lifecycle.PER_METHOD + "'."); + } + } + + private boolean isPerClassLifecycle(ExtensionContext context) { + return context.getTestInstanceLifecycle() + .filter(lifecycle -> TestInstance.Lifecycle.PER_CLASS == lifecycle) + .isPresent(); + } + + private ScenarioBase getScenario() { + return ScenarioHolder.get().getScenarioOfCurrentThread(); + } } diff --git a/jgiven-tests/build.gradle b/jgiven-tests/build.gradle index c0cff05ee8..b65bebc85a 100644 --- a/jgiven-tests/build.gradle +++ b/jgiven-tests/build.gradle @@ -3,8 +3,12 @@ description = 'JGiven Tests - Contains BDD tests for JGiven written in JGiven' dependencies { implementation project(':jgiven-junit') implementation project(':jgiven-testng') + implementation project(':jgiven-junit5') implementation project(':jgiven-html5-report') implementation "junit:junit:$junitVersion" + implementation "org.junit.jupiter:junit-jupiter-api:5.8.2" + implementation "org.junit.jupiter:junit-jupiter-engine:5.8.2" + implementation "org.junit.platform:junit-platform-runner:1.8.2" implementation "org.testng:testng:$testngVersion" implementation "com.tngtech.java:junit-dataprovider:$junitDataproviderVersion" implementation 'com.beust:jcommander:1.82' diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tags/FeatureJUnit5.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tags/FeatureJUnit5.java new file mode 100644 index 0000000000..02fdadec5b --- /dev/null +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tags/FeatureJUnit5.java @@ -0,0 +1,13 @@ +package com.tngtech.jgiven.tags; + +import com.tngtech.jgiven.annotation.IsTag; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@FeatureTestFramework +@IsTag( name = "JUnit5", + description = "Tests can be be executed with JUnit5" ) +@Retention( RetentionPolicy.RUNTIME ) +public @interface FeatureJUnit5 { + +} diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/GivenTestStage.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/GivenTestStage.java index b180fe75e5..198ee9d1bf 100644 --- a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/GivenTestStage.java +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/GivenTestStage.java @@ -7,13 +7,15 @@ public GivenTestStage an_exception_is_thrown() { throw new RuntimeException("Some Exception"); } - public void nothing() { + public GivenTestStage nothing() { + return this; } - public void a_failed_step(boolean fail) { + public GivenTestStage a_failed_step(boolean fail) { if (fail) { throw new IllegalArgumentException(); } + return this; } diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/GuaranteedFieldRealTest.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/GuaranteedFieldRealTest.java index 4cf6fabd80..21b3d6e274 100644 --- a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/GuaranteedFieldRealTest.java +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/GuaranteedFieldRealTest.java @@ -6,7 +6,8 @@ import com.tngtech.jgiven.junit.ScenarioTest; import org.junit.Test; -public class GuaranteedFieldRealTest extends ScenarioTest { +public class GuaranteedFieldRealTest extends + ScenarioTest { @Test public void a_sample_test() { diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/JGivenReportExtractingExtension.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/JGivenReportExtractingExtension.java new file mode 100644 index 0000000000..58d5b4fa7e --- /dev/null +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/JGivenReportExtractingExtension.java @@ -0,0 +1,35 @@ +package com.tngtech.jgiven.tests; + +import com.tngtech.jgiven.exception.JGivenWrongUsageException; +import com.tngtech.jgiven.impl.ScenarioBase; +import com.tngtech.jgiven.impl.ScenarioHolder; +import com.tngtech.jgiven.junit5.JGivenExtension; +import com.tngtech.jgiven.report.model.ReportModel; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import org.junit.jupiter.api.extension.ExtensionContext; + +/** + * Extracts the report Model from within the Test context before it gets deleted. + */ +public class JGivenReportExtractingExtension extends JGivenExtension { + + private static final Map, ReportModel> modelHolder = new ConcurrentHashMap<>(); + + public static Optional getReportModelFor(Class testClass) { + return Optional.ofNullable(modelHolder.get(testClass)); + } + + + @Override + public void afterTestExecution(ExtensionContext context) throws Exception { + Class testClass = context.getTestClass() + .orElseThrow(() -> new JGivenWrongUsageException("tests without test class are not supported yet")); + Optional.ofNullable(ScenarioHolder.get()) + .map(ScenarioHolder::getScenarioOfCurrentThread) + .map(ScenarioBase::getModel) + .ifPresent(model -> modelHolder.put(testClass, model)); + super.afterTestExecution(context); + } +} diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/JUnit5AfterMethodTests.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/JUnit5AfterMethodTests.java new file mode 100644 index 0000000000..6de7bbd3eb --- /dev/null +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/JUnit5AfterMethodTests.java @@ -0,0 +1,48 @@ +package com.tngtech.jgiven.tests; + +import com.tngtech.jgiven.base.ScenarioTestBase; +import com.tngtech.jgiven.impl.Scenario; +import com.tngtech.jgiven.report.model.ScenarioCaseModel; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(JGivenReportExtractingExtension.class) +public class JUnit5AfterMethodTests extends ScenarioTestBase { + + private final Scenario scenario = createScenario(); + + @Override + public Scenario getScenario() { + return scenario; + } + + @Test + public void a_failing_JUnit_5_test() { + given().nothing(); + when().a_step_fails(); + then().something_happened(); + } + + @Test + public void a_succeeding_JUnit5_test() { + given().nothing(); + when().something_happens(); + then().something_happened(); + } + + @Test + @Disabled + public void a_skipped_JUnit5_test() { + given().nothing(); + when().something_happens(); + then().something_happened(); + } + + + @AfterEach + public void modifyScenario() { + getScenario().getModel().getLastScenarioModel().addCase(new ScenarioCaseModel()); + } +} diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/ScenarioTestForTesting.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/ScenarioTestForTesting.java index 484a47a3b0..8abf462db3 100644 --- a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/ScenarioTestForTesting.java +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/ScenarioTestForTesting.java @@ -3,7 +3,9 @@ import com.tngtech.jgiven.impl.Scenario; import com.tngtech.jgiven.junit.ScenarioTest; import com.tngtech.jgiven.impl.ScenarioHolder; +import org.junit.jupiter.api.extension.ExtendWith; +@ExtendWith(JGivenReportExtractingExtension.class) public class ScenarioTestForTesting extends ScenarioTest { @Override public Scenario getScenario() { diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestClassWithDescription.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestClassWithDescription.java index 14af9f253d..43025d5151 100644 --- a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestClassWithDescription.java +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestClassWithDescription.java @@ -1,17 +1,19 @@ package com.tngtech.jgiven.tests; -import org.junit.Test; -import org.testng.annotations.Listeners; - import com.tngtech.jgiven.annotation.Description; import com.tngtech.jgiven.junit.ScenarioTest; import com.tngtech.jgiven.testng.ScenarioTestListener; +import org.junit.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.testng.annotations.Listeners; @Listeners( ScenarioTestListener.class ) +@ExtendWith(JGivenReportExtractingExtension.class) @Description( "Test Description" ) public class TestClassWithDescription extends ScenarioTest { @Test + @org.junit.jupiter.api.Test @org.testng.annotations.Test public void some_test() { given().nothing(); diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestClassWithOnlyIgnoredTests.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestClassWithOnlyIgnoredTests.java index 209b38ac8e..73b7e46dff 100644 --- a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestClassWithOnlyIgnoredTests.java +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestClassWithOnlyIgnoredTests.java @@ -1,17 +1,20 @@ package com.tngtech.jgiven.tests; +import com.tngtech.jgiven.testng.ScenarioTestListener; import org.junit.Ignore; import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.extension.ExtendWith; import org.testng.annotations.Listeners; -import com.tngtech.jgiven.junit.ScenarioTest; -import com.tngtech.jgiven.testng.ScenarioTestListener; - @Listeners( ScenarioTestListener.class ) +@ExtendWith(JGivenReportExtractingExtension.class) public class TestClassWithOnlyIgnoredTests extends ScenarioTestForTesting { @Ignore @Test + @org.junit.jupiter.api.Test + @Disabled @org.testng.annotations.Test( enabled = false ) public void test() { diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestScenarioRepository.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestScenarioRepository.java index 7961a95190..ba158ff5fe 100644 --- a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestScenarioRepository.java +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestScenarioRepository.java @@ -7,6 +7,7 @@ public class TestScenarioRepository { + public static class SearchCriteria { public boolean pending = false; public boolean failing = false; @@ -21,53 +22,53 @@ public static class SearchCriteria { public Integer numberOfParameters; public String testClassDescription; - public boolean matches( ScenarioCriteria criteria ) { - if( pending != criteria.pending) { + public boolean matches(ScenarioCriteria criteria) { + if (pending != criteria.pending) { return false; } - if( failIfPassed != null && !failIfPassed.equals( criteria.failIfPassed ) ) { + if (failIfPassed != null && !failIfPassed.equals(criteria.failIfPassed)) { return false; } - if( executeSteps != null && !executeSteps.equals( criteria.executeSteps ) ) { + if (executeSteps != null && !executeSteps.equals(criteria.executeSteps)) { return false; } - if( failing != criteria.failing ) { + if (failing != criteria.failing) { return false; } - if( numberOfSteps != null && !numberOfSteps.equals( criteria.numberOfSteps ) ) { + if (numberOfSteps != null && !numberOfSteps.equals(criteria.numberOfSteps)) { return false; } - if( numberOfFailingStages != null && !numberOfFailingStages.equals( criteria.numberOfFailingStages ) ) { + if (numberOfFailingStages != null && !numberOfFailingStages.equals(criteria.numberOfFailingStages)) { return false; } - if( failingStep != null && !failingStep.equals( criteria.failingStep ) ) { + if (failingStep != null && !failingStep.equals(criteria.failingStep)) { return false; } - if( stageWithFailingAfterStageMethod != null - && !stageWithFailingAfterStageMethod.equals( criteria.stageWithFailingAfterStageMethod ) ) { + if (stageWithFailingAfterStageMethod != null + && !stageWithFailingAfterStageMethod.equals(criteria.stageWithFailingAfterStageMethod)) { return false; } - if( tagAnnotation != null && !tagAnnotation.equals( criteria.tagAnnotation ) ) { + if (tagAnnotation != null && !tagAnnotation.equals(criteria.tagAnnotation)) { return false; } - if( parameterizedRunner != null && !parameterizedRunner.equals( criteria.parameterizedRunner ) ) { + if (parameterizedRunner != null && !parameterizedRunner.equals(criteria.parameterizedRunner)) { return false; } - if( numberOfParameters != null && !numberOfParameters.equals(criteria.numberOfParameters) ) { + if (numberOfParameters != null && !numberOfParameters.equals(criteria.numberOfParameters)) { return false; } - if( testClassDescription != null && !testClassDescription.equals( criteria.testClassDescription ) ) { + if (testClassDescription != null && !testClassDescription.equals(criteria.testClassDescription)) { return false; } @@ -109,13 +110,13 @@ public ScenarioCriteria failing() { return this; } - public ScenarioCriteria failingStep( int i ) { + public ScenarioCriteria failingStep(int i) { failing(); failingStep = i; return this; } - public ScenarioCriteria numberOfSteps( int n ) { + public ScenarioCriteria numberOfSteps(int n) { numberOfSteps = n; return this; } @@ -125,17 +126,17 @@ public ScenarioCriteria tagAnnotation() { return this; } - public ScenarioCriteria numberOfFailingStages( int i ) { + public ScenarioCriteria numberOfFailingStages(int i) { numberOfFailingStages = i; return this; } - public ScenarioCriteria stageWithFailingAfterStageMethod( Integer stageWithFailingAfterStageMethod ) { + public ScenarioCriteria stageWithFailingAfterStageMethod(Integer stageWithFailingAfterStageMethod) { this.stageWithFailingAfterStageMethod = stageWithFailingAfterStageMethod; return this; } - public ScenarioCriteria numberOfParameters( int n ) { + public ScenarioCriteria numberOfParameters(int n) { this.numberOfParameters = n; return this; } @@ -145,7 +146,7 @@ public ScenarioCriteria parameterizedRunner() { return this; } - public ScenarioCriteria testClassDescription( String value ) { + public ScenarioCriteria testClassDescription(String value) { this.testClassDescription = value; return this; } @@ -156,16 +157,16 @@ public static class TestScenario { public String testMethod; public ScenarioCriteria criteria = new ScenarioCriteria(); - public TestScenario( Class testClass ) { + public TestScenario(Class testClass) { this.testClass = testClass; } - public TestScenario( String testMethod ) { + public TestScenario(String testMethod) { this.testMethod = testMethod; this.testClass = TestScenarios.class; } - public TestScenario( Class testClass, String testMethod ) { + public TestScenario(Class testClass, String testMethod) { this.testClass = testClass; this.testMethod = testMethod; } @@ -173,109 +174,116 @@ public TestScenario( Class testClass, String testMethod ) { final static List testScenarios = setupTestScenarios(); - public static TestScenario findScenario( SearchCriteria searchCriteria ) { - for( TestScenario scenario : testScenarios ) { - if( searchCriteria.matches( scenario.criteria ) ) { + public static TestScenario findScenario(SearchCriteria searchCriteria) { + for (TestScenario scenario : testScenarios) { + if (searchCriteria.matches(scenario.criteria)) { return scenario; } } - throw new IllegalArgumentException( "No matching scenario found" ); + throw new IllegalArgumentException("No matching scenario found"); } - private static ScenarioCriteria addTestScenario( List list, Class testClass ) { - TestScenario testScenario = new TestScenario( testClass ); - list.add( testScenario ); + private static ScenarioCriteria addTestScenario(List list, Class testClass) { + TestScenario testScenario = new TestScenario(testClass); + list.add(testScenario); return testScenario.criteria; } - private static ScenarioCriteria addTestScenario( List list, String testMethod ) { - TestScenario testScenario = new TestScenario( testMethod ); - list.add( testScenario ); + private static ScenarioCriteria addTestScenario(List list, String testMethod) { + TestScenario testScenario = new TestScenario(testMethod); + list.add(testScenario); return testScenario.criteria; } - private static ScenarioCriteria addTestScenario( List list, Class testClass, String testMethod ) { - TestScenario testScenario = new TestScenario( testClass ); + private static ScenarioCriteria addTestScenario(List list, Class testClass, String testMethod) { + TestScenario testScenario = new TestScenario(testClass); testScenario.testMethod = testMethod; - list.add( testScenario ); + list.add(testScenario); return testScenario.criteria; } private static List setupTestScenarios() { List result = Lists.newArrayList(); - addTestScenario( result, "failing_test_with_two_steps" ) - .numberOfSteps( 2 ) - .failingStep( 1 ); + addTestScenario(result, "failing_test_with_two_steps") + .numberOfSteps(2) + .failingStep(1); - addTestScenario( result, "failing_test_with_three_steps" ) - .numberOfSteps( 3 ) - .failingStep( 1 ); + addTestScenario(result, "failing_test_with_three_steps") + .numberOfSteps(3) + .failingStep(1); - addTestScenario( result, "failing_test_with_two_steps_and_second_step_fails" ) - .numberOfSteps( 2 ) - .failingStep( 2 ); + addTestScenario(result, "failing_test_with_two_steps_and_second_step_fails") + .numberOfSteps(2) + .failingStep(2); - addTestScenario( result, "failing_test_with_two_failing_stages" ) - .numberOfSteps( 2 ) - .numberOfFailingStages( 2 ) - .failingStep( 1 ); + addTestScenario(result, "failing_test_with_two_failing_stages") + .numberOfSteps(2) + .numberOfFailingStages(2) + .failingStep(1); - addTestScenario( result, "failing_test_where_second_stage_has_a_failing_after_stage_method" ) - .numberOfSteps( 2 ) - .numberOfFailingStages( 2 ) - .stageWithFailingAfterStageMethod( 2 ) - .failingStep( 1 ); + addTestScenario(result, "failing_test_where_second_stage_has_a_failing_after_stage_method") + .numberOfSteps(2) + .numberOfFailingStages(2) + .stageWithFailingAfterStageMethod(2) + .failingStep(1); - addTestScenario( result, "failing_test_with_Pending_annotation" ) + addTestScenario(result, "failing_test_with_Pending_annotation") .pending() - .numberOfSteps( 2 ) - .failingStep( 1 ); + .numberOfSteps(2) + .failingStep(1); - addTestScenario( result, "passing_test_with_Pending_annotation" ) + addTestScenario(result, "passing_test_with_Pending_annotation") .pending(); - addTestScenario( result, "passing_test_with_Pending_annotation_and_failIfPassed_set_to_true" ) + addTestScenario(result, "passing_test_with_Pending_annotation_and_failIfPassed_set_to_true") .pending() .failIfPassed(); - addTestScenario( result, "failing_test_with_Pending_annotation_and_failIfPassed_set_to_true" ) + addTestScenario(result, "failing_test_with_Pending_annotation_and_failIfPassed_set_to_true") .pending() .failIfPassed() - .failingStep( 1 ); + .failingStep(1); - addTestScenario( result, "failing_test_with_Pending_annotation_and_executeSteps_set_to_true" ) + addTestScenario(result, "failing_test_with_Pending_annotation_and_executeSteps_set_to_true") .pending() .executeSteps() - .failingStep( 1 ); + .failingStep(1); - addTestScenario( result, "test_with_tag_annotation" ) + addTestScenario(result, "test_with_tag_annotation") .tagAnnotation(); - addTestScenario( result, TestClassWithParameterizedRunner.class ) + addTestScenario(result, TestClassWithParameterizedRunner.class) .parameterizedRunner() - .numberOfParameters( 2 ); + .numberOfParameters(2); - addTestScenario( result, TestClassWithDescription.class, "some_test" ) - .testClassDescription( TestClassWithDescription.class.getAnnotation( Description.class ).value() ); + addTestScenario(result, TestClassWithDescription.class, "some_test") + .testClassDescription(TestClassWithDescription.class.getAnnotation(Description.class).value()); return result; } public static TestScenario testClassWithOnlyIgnoredTests() { - return new TestScenario( TestClassWithOnlyIgnoredTests.class ); + return new TestScenario(TestClassWithOnlyIgnoredTests.class); } public static TestScenario testClassWithAFailingScenarioAndAFailingAfterStage() { - return new TestScenario( TestWithExceptionsInAfterMethod.class ); + return new TestScenario(TestWithExceptionsInAfterMethod.class); } public static TestScenario testWithTwoCasesAndTheFirstOneFails() { - return new TestScenario( TestWithTwoCasesAndAFailingOne.class ); + return new TestScenario(TestWithTwoCasesAndAFailingOne.class); + } + + public static TestScenario junit5TestsWithModificationsInAfterMethod() { + return new TestScenario(JUnit5AfterMethodTests.class); } public static TestScenario testNgTestWithAFailingCase() { - return new TestScenario( FailingCasesTestNgTest.class ); + return new TestScenario(FailingCasesTestNgTest.class); } + public static TestScenario junit5TestClassWithPerClassLifecycle() { + return new TestScenario(TestWithPerClassLifecycle.class); + } } diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestScenarios.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestScenarios.java index b4cc2733d4..c200cc6673 100644 --- a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestScenarios.java +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestScenarios.java @@ -1,15 +1,17 @@ package com.tngtech.jgiven.tests; -import org.junit.Test; -import org.testng.annotations.Listeners; - import com.tngtech.jgiven.annotation.Pending; import com.tngtech.jgiven.testng.ScenarioTestListener; +import org.junit.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.testng.annotations.Listeners; @Listeners( ScenarioTestListener.class ) +@ExtendWith(JGivenReportExtractingExtension.class) public class TestScenarios extends ScenarioTestForTesting { @Test + @org.junit.jupiter.api.Test @org.testng.annotations.Test public void failing_test_with_two_steps() { given().an_exception_is_thrown(); @@ -17,6 +19,7 @@ public void failing_test_with_two_steps() { } @Test + @org.junit.jupiter.api.Test @org.testng.annotations.Test public void failing_test_with_three_steps() { given().an_exception_is_thrown(); @@ -25,6 +28,7 @@ public void failing_test_with_three_steps() { } @Test + @org.junit.jupiter.api.Test @org.testng.annotations.Test public void failing_test_with_two_steps_and_second_step_fails() { given().nothing(); @@ -32,6 +36,7 @@ public void failing_test_with_two_steps_and_second_step_fails() { } @Test + @org.junit.jupiter.api.Test @org.testng.annotations.Test public void failing_test_with_two_failing_stages() { given().an_exception_is_thrown(); @@ -39,6 +44,7 @@ public void failing_test_with_two_failing_stages() { } @Test + @org.junit.jupiter.api.Test @org.testng.annotations.Test public void failing_test_where_second_stage_has_a_failing_after_stage_method() { FailingAfterStageMethodStage stage = addStage( FailingAfterStageMethodStage.class ); @@ -47,6 +53,7 @@ public void failing_test_where_second_stage_has_a_failing_after_stage_method() { } @Test + @org.junit.jupiter.api.Test @org.testng.annotations.Test @Pending public void failing_test_with_Pending_annotation() { @@ -55,6 +62,7 @@ public void failing_test_with_Pending_annotation() { } @Test + @org.junit.jupiter.api.Test @org.testng.annotations.Test @Pending public void passing_test_with_Pending_annotation() { @@ -62,6 +70,7 @@ public void passing_test_with_Pending_annotation() { } @Test + @org.junit.jupiter.api.Test @org.testng.annotations.Test @Pending( failIfPass = true ) public void passing_test_with_Pending_annotation_and_failIfPassed_set_to_true() { @@ -69,6 +78,7 @@ public void passing_test_with_Pending_annotation_and_failIfPassed_set_to_true() } @Test + @org.junit.jupiter.api.Test @org.testng.annotations.Test @Pending( failIfPass = true ) public void failing_test_with_Pending_annotation_and_failIfPassed_set_to_true() { @@ -76,6 +86,7 @@ public void failing_test_with_Pending_annotation_and_failIfPassed_set_to_true() } @Test + @org.junit.jupiter.api.Test @org.testng.annotations.Test @Pending( failIfPass = true, executeSteps = true ) public void failing_test_with_Pending_annotation_and_executeSteps_set_to_true() { @@ -83,6 +94,7 @@ public void failing_test_with_Pending_annotation_and_executeSteps_set_to_true() } @Test + @org.junit.jupiter.api.Test @org.testng.annotations.Test @TestTag( "testValue" ) public void test_with_tag_annotation() { diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestWithExceptionsInAfterMethod.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestWithExceptionsInAfterMethod.java index 1371bd5ed8..a9dd7e557d 100644 --- a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestWithExceptionsInAfterMethod.java +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestWithExceptionsInAfterMethod.java @@ -2,17 +2,21 @@ import org.junit.After; import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.extension.ExtendWith; -import com.tngtech.jgiven.junit.ScenarioTest; - -public class TestWithExceptionsInAfterMethod extends ScenarioTestForTesting { +@ExtendWith(JGivenReportExtractingExtension.class) +public class TestWithExceptionsInAfterMethod + extends ScenarioTestForTesting { @After + @AfterEach public void afterException() { - throw new IllegalStateException( "exception in after method" ); + throw new IllegalStateException("exception in after method"); } @Test + @org.junit.jupiter.api.Test public void test_that_exception_in_scenario_is_not_hidden_by_exception_in_JUnit_after_method() { given().nothing(); when().a_step_fails(); diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestWithPerClassLifecycle.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestWithPerClassLifecycle.java new file mode 100644 index 0000000000..c82d81748b --- /dev/null +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/TestWithPerClassLifecycle.java @@ -0,0 +1,16 @@ +package com.tngtech.jgiven.tests; + + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class TestWithPerClassLifecycle extends ScenarioTestForTesting { + + @Test + void innocuous_jgiven_test() { + given().nothing(); + when().something_happens(); + then().something_happened(); + } +} diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/WhenTestStage.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/WhenTestStage.java index 24f4810931..8a46fe0adb 100644 --- a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/WhenTestStage.java +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/WhenTestStage.java @@ -8,7 +8,7 @@ public WhenTestStage something_happens() { return self(); } - public void a_step_fails() { + public WhenTestStage a_step_fails() { throw new AssertionError( "assertion failed in test step" ); } diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/GivenScenarioTest.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/GivenScenarioTest.java index 15be14a41e..f65cab5f07 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/GivenScenarioTest.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/GivenScenarioTest.java @@ -40,12 +40,12 @@ public SELF a_failing_test() { return self(); } - public SELF the_test_has_$_failing_stages( int n ) { + public SELF the_test_has_$_failing_stages(int n) { criteria.numberOfFailingStages = n; return self(); } - public SELF stage_$_has_a_failing_after_stage_method( int i ) { + public SELF stage_$_has_a_failing_after_stage_method(int i) { criteria.stageWithFailingAfterStageMethod = i; return self(); } @@ -65,35 +65,35 @@ public SELF executeSteps_set_to_true() { return self(); } - public SELF the_test_has_a_tag_annotation_named( String name ) { - assertThat( name ).isEqualTo( "TestTag" ); + public SELF the_test_has_a_tag_annotation_named(String name) { + assertThat(name).isEqualTo("TestTag"); criteria.tagAnnotation = true; return self(); } @AfterStage public void findScenario() { - if( testScenario == null ) { - testScenario = TestScenarioRepository.findScenario( criteria ); + if (testScenario == null) { + testScenario = TestScenarioRepository.findScenario(criteria); } } - public SELF a_failing_test_with_$_steps( int n ) { + public SELF a_failing_test_with_$_steps(int n) { a_failing_test(); - return a_test_with_$_steps( n ); + return a_test_with_$_steps(n); } - public SELF a_test_with_$_steps( int n ) { + public SELF a_test_with_$_steps(int n) { criteria.numberOfSteps = n; return self(); } - public SELF step_$_fails( int i ) { + public SELF step_$_fails(int i) { criteria.failingStep = i; return self(); } - public SELF the_test_class_has_a_description_annotation_with_value( String value ) { + public SELF the_test_class_has_a_description_annotation_with_value(String value) { criteria.testClassDescription = value; return self(); } @@ -103,7 +103,7 @@ public SELF a_JUnit_test_class_with_the_Parameterized_Runner() { return self(); } - public SELF the_test_class_has_$_parameters( int nParameters ) { + public SELF the_test_class_has_$_parameters(int nParameters) { criteria.numberOfParameters = nParameters; return self(); } @@ -127,4 +127,14 @@ public void a_TestNG_test_with_two_cases_and_the_first_one_fails() { public void a_pending_scenario_with_a_data_provider() { testScenario = new TestScenario(PendingDataProviderTests.class); } + + public SELF junit5_tests_with_scenario_modifications_in_after_method() { + testScenario = TestScenarioRepository.junit5TestsWithModificationsInAfterMethod(); + return self(); + } + + public SELF junit5_test_class_with_a_per_class_lifecycle(){ + testScenario = TestScenarioRepository.junit5TestClassWithPerClassLifecycle(); + return self(); + } } diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/RepeatedStageUseTest.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/RepeatedStageUseTest.java similarity index 97% rename from jgiven-tests/src/test/java/com/tngtech/jgiven/junit/RepeatedStageUseTest.java rename to jgiven-tests/src/test/java/com/tngtech/jgiven/impl/RepeatedStageUseTest.java index 5ec034b1f1..7cc519765b 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/RepeatedStageUseTest.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/RepeatedStageUseTest.java @@ -1,4 +1,4 @@ -package com.tngtech.jgiven.junit; +package com.tngtech.jgiven.impl; import static org.assertj.core.api.Assertions.assertThat; @@ -9,6 +9,7 @@ import com.tngtech.jgiven.annotation.ProvidedScenarioState; import com.tngtech.jgiven.annotation.ScenarioStage; import com.tngtech.jgiven.annotation.ScenarioState; +import com.tngtech.jgiven.junit.ScenarioTest; import org.junit.Test; public class RepeatedStageUseTest extends ScenarioTest< diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/SimpleStageRepetitionUseTest.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/SimpleStageRepetitionUseTest.java similarity index 97% rename from jgiven-tests/src/test/java/com/tngtech/jgiven/junit/SimpleStageRepetitionUseTest.java rename to jgiven-tests/src/test/java/com/tngtech/jgiven/impl/SimpleStageRepetitionUseTest.java index 638ffc8c76..6c6cee4daa 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/SimpleStageRepetitionUseTest.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/SimpleStageRepetitionUseTest.java @@ -1,4 +1,4 @@ -package com.tngtech.jgiven.junit; +package com.tngtech.jgiven.impl; import static org.assertj.core.api.Assertions.assertThat; @@ -6,6 +6,7 @@ import com.tngtech.jgiven.annotation.AfterStage; import com.tngtech.jgiven.annotation.BeforeStage; import com.tngtech.jgiven.annotation.ScenarioStage; +import com.tngtech.jgiven.junit.SimpleScenarioTest; import org.junit.Ignore; import org.junit.Test; diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/JUnitExecutor.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/JUnitExecutor.java index ac619ecc44..3213d36e74 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/JUnitExecutor.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/JUnitExecutor.java @@ -41,7 +41,7 @@ public Request supply() { } @Override - public TestExecutionResult execute( final Class testClass ) { + public TestExecutionResult execute(final Class testClass ) { return execute( new RequestSupplier() { @Override public Request supply() { diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/JUnitExecutorTest.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/JUnitExecutorTest.java index 637c205695..d3dc4283e4 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/JUnitExecutorTest.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/junit/JUnitExecutorTest.java @@ -10,19 +10,20 @@ import com.tngtech.jgiven.testframework.WhenTestFramework; @FeatureJUnit -public class JUnitExecutorTest extends JGivenScenarioTest, WhenTestFramework, ThenTestFramework> { +public class JUnitExecutorTest + extends JGivenScenarioTest, WhenTestFramework, ThenTestFramework> { @Test public void the_JUnit_Parametrized_runner_creates_correct_cases() { given().a_JUnit_test_class_with_the_Parameterized_Runner() - .and().the_test_class_has_$_parameters( 2 ); + .and().the_test_class_has_$_parameters(2); when().the_test_class_is_executed_with_JUnit(); then().the_report_model_contains_one_scenario_for_each_test_method() - .and().each_scenario_contains_$_cases( 2 ); + .and().each_scenario_contains_$_cases(2); } @Test - @Issue( "#25" ) + @Issue("#25") public void test_classes_with_only_ignored_test_result_in_a_valid_report() { given().a_test_class_with_all_tests_ignored(); when().the_test_class_is_executed_with_JUnit(); @@ -31,44 +32,44 @@ public void test_classes_with_only_ignored_test_result_in_a_valid_report() { } @Test - @Issue( "#49" ) + @Issue("#49") public void exception_in_scenario_is_not_hidden_by_exception_in_JUnit_after_method() { given().a_test_class_with_a_failing_scenario_and_a_failing_after_stage(); when().the_test_class_is_executed_with_JUnit(); - then().the_test_fails_with_message( "assertion failed in test step" ); + then().the_test_fails_with_message("assertion failed in test step"); } @Test public void steps_following_failing_steps_are_reported_as_skipped() { - given().a_failing_test_with_$_steps( 3 ) - .and().step_$_fails( 1 ); + given().a_failing_test_with_$_steps(3) + .and().step_$_fails(1); when().the_test_is_executed_with_JUnit(); - then().step_$_is_reported_as_failed( 1 ) - .and().step_$_is_reported_as_skipped( 2 ) - .and().step_$_is_reported_as_skipped( 3 ); + then().step_$_is_reported_as_failed(1) + .and().step_$_is_reported_as_skipped(2) + .and().step_$_is_reported_as_skipped(3); } @Test public void after_stage_methods_of_stages_following_failing_stages_are_ignored() { - given().a_failing_test_with_$_steps( 2 ) - .and().the_test_has_$_failing_stages( 2 ) - .and().stage_$_has_a_failing_after_stage_method( 2 ) - .and().step_$_fails( 1 ); + given().a_failing_test_with_$_steps(2) + .and().the_test_has_$_failing_stages(2) + .and().stage_$_has_a_failing_after_stage_method(2) + .and().step_$_fails(1); when().the_test_is_executed_with_JUnit(); then().the_test_fails() - .and().step_$_is_reported_as_failed( 1 ) - .and().step_$_is_reported_as_skipped( 2 ); + .and().step_$_is_reported_as_failed(1) + .and().step_$_is_reported_as_skipped(2); } @Test public void all_steps_of_stages_following_failing_stages_are_ignored() { - given().a_failing_test_with_$_steps( 2 ) - .and().the_test_has_$_failing_stages( 2 ) - .and().step_$_fails( 1 ); + given().a_failing_test_with_$_steps(2) + .and().the_test_has_$_failing_stages(2) + .and().step_$_fails(1); when().the_test_is_executed_with_JUnit(); then().the_test_fails() - .and().step_$_is_reported_as_failed( 1 ) - .and().step_$_is_reported_as_skipped( 2 ); + .and().step_$_is_reported_as_failed(1) + .and().step_$_is_reported_as_skipped(2); } diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/junit5/JUnit5Executor.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/junit5/JUnit5Executor.java new file mode 100644 index 0000000000..d1959d7816 --- /dev/null +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/junit5/JUnit5Executor.java @@ -0,0 +1,39 @@ +package com.tngtech.jgiven.junit5; + +import com.tngtech.jgiven.testframework.TestExecutionResult; +import com.tngtech.jgiven.testframework.TestExecutor; +import org.junit.platform.engine.discovery.DiscoverySelectors; +import org.junit.platform.launcher.Launcher; +import org.junit.platform.launcher.LauncherDiscoveryRequest; +import org.junit.platform.launcher.LauncherSession; +import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder; +import org.junit.platform.launcher.core.LauncherFactory; + +public class JUnit5Executor extends TestExecutor { + + @Override + public TestExecutionResult execute(Class testClass, String testMethod) { + LauncherDiscoveryRequest launcherRequest = LauncherDiscoveryRequestBuilder.request() + .selectors(DiscoverySelectors.selectMethod(testClass, testMethod)) + .build(); + return run(launcherRequest); + } + + @Override + public TestExecutionResult execute(Class testClass) { + LauncherDiscoveryRequest launcherRequest = + LauncherDiscoveryRequestBuilder.request().selectors(DiscoverySelectors.selectClass(testClass)).build(); + return run(launcherRequest); + } + + private TestExecutionResult run(LauncherDiscoveryRequest launchRequest) { + TestExecutionResultProvider listener = new TestExecutionResultProvider(); + + try (LauncherSession session = LauncherFactory.openSession()) { + Launcher launcher = session.getLauncher(); + launcher.registerTestExecutionListeners(listener); + launcher.execute(launchRequest); + return listener.getExecutionResult(); + } + } +} diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/junit5/JUnit5ExecutorTest.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/junit5/JUnit5ExecutorTest.java new file mode 100644 index 0000000000..783827b185 --- /dev/null +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/junit5/JUnit5ExecutorTest.java @@ -0,0 +1,79 @@ +package com.tngtech.jgiven.junit5; + +import com.tngtech.jgiven.GivenScenarioTest; +import com.tngtech.jgiven.JGivenScenarioTest; +import com.tngtech.jgiven.tags.FeatureJUnit5; +import com.tngtech.jgiven.tags.Issue; +import com.tngtech.jgiven.testframework.ThenTestFramework; +import com.tngtech.jgiven.testframework.WhenTestFramework; +import org.junit.Test; + +@FeatureJUnit5 +public class JUnit5ExecutorTest + extends JGivenScenarioTest, WhenTestFramework, ThenTestFramework> { + + + @Test + public void tests_with_scenario_modifications_in_after_method() { + given().junit5_tests_with_scenario_modifications_in_after_method(); + when().the_test_class_is_executed_with_JUnit5(); + then().each_scenario_contains_$_cases(2); + } + + @Test + public void tests_with_a_per_class_lifecycle() { + given().junit5_test_class_with_a_per_class_lifecycle(); + when().the_test_class_is_executed_with_JUnit5(); + then().the_test_fails_with_message("JGiven does not support keeping a test instance over multiple scenarios"); + } + + @Test + @Issue("#25") + public void a_valid_report_is_generated_for_classes_that_are_disabled() { + given().a_test_class_with_all_tests_ignored(); + when().the_test_class_is_executed_with_JUnit5(); + then().the_report_model_is_either_null_or_empty() + .and().has_a_valid_class_name_if_it_is_not_null(); + } + + @Test + @Issue("#49") + public void exception_in_scenario_is_not_hidden_by_exception_in_JUnit_after_method() { + given().a_test_class_with_a_failing_scenario_and_a_failing_after_stage(); + when().the_test_class_is_executed_with_JUnit5(); + then().the_test_fails_with_message("assertion failed in test step"); + } + + @Test + public void steps_following_failing_steps_are_reported_as_skipped() { + given().a_failing_test_with_$_steps(3) + .and().step_$_fails(1); + when().the_test_is_executed_with_JUnit5(); + then().step_$_is_reported_as_failed(1) + .and().step_$_is_reported_as_skipped(2) + .and().step_$_is_reported_as_skipped(3); + } + + @Test + public void after_stage_methods_of_stages_following_failing_stages_are_ignored() { + given().a_failing_test_with_$_steps(2) + .and().the_test_has_$_failing_stages(2) + .and().stage_$_has_a_failing_after_stage_method(2) + .and().step_$_fails(1); + when().the_test_is_executed_with_JUnit5(); + then().the_test_fails() + .and().step_$_is_reported_as_failed(1) + .and().step_$_is_reported_as_skipped(2); + } + + @Test + public void all_steps_of_stages_following_failing_stages_are_ignored() { + given().a_failing_test_with_$_steps(2) + .and().the_test_has_$_failing_stages(2) + .and().step_$_fails(1); + when().the_test_is_executed_with_JUnit(); + then().the_test_fails() + .and().step_$_is_reported_as_failed(1) + .and().step_$_is_reported_as_skipped(2); + } +} diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/junit5/Junit5TestExecutionResult.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/junit5/Junit5TestExecutionResult.java new file mode 100644 index 0000000000..9db7f16434 --- /dev/null +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/junit5/Junit5TestExecutionResult.java @@ -0,0 +1,68 @@ +package com.tngtech.jgiven.junit5; + +import com.tngtech.jgiven.report.model.ReportModel; +import com.tngtech.jgiven.testframework.TestExecutionResult; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.stream.Stream; + +class Junit5TestExecutionResult extends TestExecutionResult { + + private final ReportModel report; + private final Map methodResults; + private final Map, org.junit.platform.engine.TestExecutionResult> classResults; + + public Junit5TestExecutionResult(ReportModel report, + Map result, + Map, org.junit.platform.engine.TestExecutionResult> classResults) { + this.report = report; + this.methodResults = result; + this.classResults = classResults; + } + + + @Override + public ReportModel getReportModel() { + return report; + } + + @Override + public int getFailureCount() { + return (int) Stream.concat(methodResults.values().stream(), classResults.values().stream()) + .filter(result -> result.getStatus() == org.junit.platform.engine.TestExecutionResult.Status.FAILED) + .count(); + } + + @Override + public String getFailureMessage(int i) { + if (i == 0 && getFailureCount() == 1) { + return assumeCallerRequestsTheOnlyFailureThatExists(); + } else { + throw new UnsupportedOperationException("Cannot address a map by Index." + + "This method is a result of attempting to adapt to data model that was focused on JUnit4. " + + "Please refactor."); + } + } + + private String assumeCallerRequestsTheOnlyFailureThatExists() { + if (noFailureInClassResults()) { + return getFailureMessageFromTestResults(methodResults); + } else { + return getFailureMessageFromTestResults(classResults); + } + } + + private boolean noFailureInClassResults() { + return classResults.values().stream() + .map(org.junit.platform.engine.TestExecutionResult::getStatus) + .allMatch(status -> status == org.junit.platform.engine.TestExecutionResult.Status.SUCCESSFUL); + } + + private String getFailureMessageFromTestResults(Map results) { + return results.values().stream() + .findFirst() + .flatMap(org.junit.platform.engine.TestExecutionResult::getThrowable) + .map(Throwable::getMessage) + .orElseThrow(() -> new IllegalStateException("Expected failure message for test 1, but none present.")); + } +} diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/junit5/TestExecutionResultProvider.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/junit5/TestExecutionResultProvider.java new file mode 100644 index 0000000000..3ad53f902e --- /dev/null +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/junit5/TestExecutionResultProvider.java @@ -0,0 +1,58 @@ +package com.tngtech.jgiven.junit5; + +import com.tngtech.jgiven.report.model.ReportModel; +import com.tngtech.jgiven.testframework.TestExecutionResult; +import com.tngtech.jgiven.tests.JGivenReportExtractingExtension; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import org.junit.platform.engine.TestSource; +import org.junit.platform.engine.support.descriptor.ClassSource; +import org.junit.platform.engine.support.descriptor.MethodSource; +import org.junit.platform.launcher.TestExecutionListener; +import org.junit.platform.launcher.TestIdentifier; + +class TestExecutionResultProvider implements TestExecutionListener { + + private final Map methodExecutionResults = new HashMap<>(); + private final Map, org.junit.platform.engine.TestExecutionResult> classExecutionResults = new HashMap<>(); + + + private ReportModel reportModel; + + @Override + public void executionFinished(TestIdentifier testIdentifier, + org.junit.platform.engine.TestExecutionResult testExecutionResult) { + TestSource testSource = testIdentifier.getSource().orElse(null); + if (testSource instanceof MethodSource) { + handleTestMethodFinished((MethodSource) testSource, testExecutionResult); + } else if (testSource instanceof ClassSource) { + handleTestClassFinished((ClassSource) testSource, testExecutionResult); + } + } + + private void handleTestClassFinished(ClassSource testSource, + org.junit.platform.engine.TestExecutionResult testExecutionResult) { + classExecutionResults.put(testSource.getJavaClass(), testExecutionResult); + setReportModel(testSource); + } + + private void setReportModel(ClassSource testSource) { + if (methodExecutionResults.size() == 0) { + this.reportModel = null; + } else { + this.reportModel = JGivenReportExtractingExtension.getReportModelFor(testSource.getJavaClass()) + .orElseThrow(() -> new IllegalStateException("Failed to obtain report model for tested class.")); + } + } + + private void handleTestMethodFinished(MethodSource testSource, + org.junit.platform.engine.TestExecutionResult testExecutionResult) { + methodExecutionResults.put(testSource.getJavaMethod(), testExecutionResult); + } + + + public TestExecutionResult getExecutionResult() { + return new Junit5TestExecutionResult(reportModel, methodExecutionResults, classExecutionResults); + } +} diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/TestExecutor.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/TestExecutor.java index 3e0e2174e3..cd1bb18e29 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/TestExecutor.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/TestExecutor.java @@ -1,22 +1,25 @@ package com.tngtech.jgiven.testframework; import com.tngtech.jgiven.junit.JUnitExecutor; +import com.tngtech.jgiven.junit5.JUnit5Executor; import com.tngtech.jgiven.testng.TestNgExecutor; public abstract class TestExecutor { - public static TestExecutor getExecutor( TestFramework framework ) { - switch( framework ) { + public static TestExecutor getExecutor(TestFramework framework) { + switch (framework) { case JUnit: return new JUnitExecutor(); + case JUnit5: + return new JUnit5Executor(); case TestNG: return new TestNgExecutor(); default: - throw new IllegalArgumentException( "Unknown framework: " + framework ); + throw new IllegalArgumentException("Unknown framework: " + framework); } } - public abstract TestExecutionResult execute( Class testClass, String testMethod ); + public abstract TestExecutionResult execute(Class testClass, String testMethod); - public abstract TestExecutionResult execute( Class testClass ); + public abstract TestExecutionResult execute(Class testClass); } diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/TestFramework.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/TestFramework.java index 3082a31eae..4423e4eaa9 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/TestFramework.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/TestFramework.java @@ -2,5 +2,6 @@ public enum TestFramework { JUnit, + JUnit5, TestNG -} \ No newline at end of file +} diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/TestFrameworkExecutionTest.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/TestFrameworkExecutionTest.java index ce856ffd89..1c198c6de8 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/TestFrameworkExecutionTest.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/TestFrameworkExecutionTest.java @@ -1,36 +1,37 @@ package com.tngtech.jgiven.testframework; -import java.util.Arrays; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - import com.tngtech.jgiven.GivenScenarioTest; import com.tngtech.jgiven.JGivenScenarioTest; import com.tngtech.jgiven.tags.FeatureJUnit; +import com.tngtech.jgiven.tags.FeatureJUnit5; import com.tngtech.jgiven.tags.FeaturePending; import com.tngtech.jgiven.tags.FeatureTags; import com.tngtech.jgiven.tags.FeatureTestNg; import com.tngtech.jgiven.tags.Issue; +import java.util.Arrays; +import java.util.stream.Collectors; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +@FeatureJUnit5 @FeatureJUnit @FeatureTestNg -@RunWith( Parameterized.class ) -public class TestFrameworkExecutionTest extends JGivenScenarioTest, WhenTestFramework, ThenTestFramework> { +@RunWith(Parameterized.class) +public class TestFrameworkExecutionTest + extends JGivenScenarioTest, WhenTestFramework, ThenTestFramework> { public final TestFramework testFramework; @Parameters public static Iterable testFrameworks() { - return Arrays.asList( new Object[][] { - { TestFramework.JUnit }, - { TestFramework.TestNG }, - } ); + return Arrays.stream(TestFramework.values()) + .map(value -> new Object[] {value}) + .collect(Collectors.toList()); } - public TestFrameworkExecutionTest( TestFramework testFramework ) { + public TestFrameworkExecutionTest(TestFramework testFramework) { this.testFramework = testFramework; } @@ -39,7 +40,7 @@ public TestFrameworkExecutionTest( TestFramework testFramework ) { public void failing_tests_annotated_with_Pending_are_ignored() { given().a_failing_test() .and().the_test_is_annotated_with_Pending(); - when().the_test_is_executed_with( testFramework ); + when().the_test_is_executed_with(testFramework); then().the_test_is_ignored(); } @@ -48,29 +49,30 @@ public void failing_tests_annotated_with_Pending_are_ignored() { public void passing_tests_annotated_with_Pending_are_ignored() { given().a_passing_test() .and().the_test_is_annotated_with_Pending(); - when().the_test_is_executed_with( testFramework ); + when().the_test_is_executed_with(testFramework); then().the_test_is_ignored(); } @Test - @Issue( "#4" ) + @Issue("#4") @FeaturePending public void passing_tests_annotated_with_Pending_with_failIfPassed_set_to_true_fail() { given().a_passing_test() .and().the_test_is_annotated_with_Pending() .with().failIfPassed_set_to_true(); - when().the_test_is_executed_with( testFramework ); - then().the_test_fails_with_message( "Test succeeded, but failIfPassed set to true. Now might be the right time to remove the @Pending annotation." ); + when().the_test_is_executed_with(testFramework); + then().the_test_fails_with_message( + "Test succeeded, but failIfPassed set to true. Now might be the right time to remove the @Pending annotation."); } @Test - @Issue( "#4" ) + @Issue("#4") @FeaturePending public void failing_tests_annotated_with_Pending_with_failIfPassed_set_to_true_are_ignored() { given().a_failing_test() .and().the_test_is_annotated_with_Pending() .with().failIfPassed_set_to_true(); - when().the_test_is_executed_with( testFramework ); + when().the_test_is_executed_with(testFramework); then().the_test_is_ignored(); } @@ -80,23 +82,23 @@ public void failing_tests_annotated_with_Pending_with_executeSteps_set_to_true_a given().a_failing_test() .and().the_test_is_annotated_with_Pending() .with().executeSteps_set_to_true(); - when().the_test_is_executed_with( testFramework ); + when().the_test_is_executed_with(testFramework); then().the_test_is_ignored(); } @Test public void passing_steps_before_failing_steps_are_reported_as_passed() { - given().a_failing_test_with_$_steps( 2 ) - .and().step_$_fails( 2 ); - when().the_test_is_executed_with( testFramework ); - then().step_$_is_reported_as_passed( 1 ) - .and().step_$_is_reported_as_failed( 2 ); + given().a_failing_test_with_$_steps(2) + .and().step_$_fails(2); + when().the_test_is_executed_with(testFramework); + then().step_$_is_reported_as_passed(1) + .and().step_$_is_reported_as_failed(2); } @Test public void the_error_message_of_a_failing_step_is_reported() { given().a_failing_test(); - when().the_test_is_executed_with( testFramework ); + when().the_test_is_executed_with(testFramework); then().the_case_is_marked_as_failed() .and().an_error_message_is_stored_in_the_report(); } @@ -105,16 +107,16 @@ public void the_error_message_of_a_failing_step_is_reported() { @FeatureTags public void tag_annotations_appear_in_the_report_model() { given().a_test() - .and().the_test_has_a_tag_annotation_named( "TestTag" ); - when().the_test_is_executed_with( testFramework ); - then().the_report_model_contains_a_tag_named( "com.tngtech.jgiven.tests.TestTag" ); + .and().the_test_has_a_tag_annotation_named("TestTag"); + when().the_test_is_executed_with(testFramework); + then().the_report_model_contains_a_tag_named("com.tngtech.jgiven.tests.TestTag"); } @Test public void description_annotations_on_test_classes_are_evaluated() { given().a_test_class() - .and().the_test_class_has_a_description_annotation_with_value( "Test Description" ); - when().the_test_is_executed_with( testFramework ); - then().the_description_of_the_report_model_is( "Test Description" ); + .and().the_test_class_has_a_description_annotation_with_value("Test Description"); + when().the_test_is_executed_with(testFramework); + then().the_description_of_the_report_model_is("Test Description"); } } diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/ThenTestFramework.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/ThenTestFramework.java index 6a5b2c005a..785bb4af48 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/ThenTestFramework.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/ThenTestFramework.java @@ -29,53 +29,54 @@ public SELF the_test_is_ignored() { } public SELF the_test_passes() { - assertThat( result.getFailureCount() ).as( "failure count" ).isEqualTo( 0 ); + assertThat(result.getFailureCount()).as("failure count").isEqualTo(0); return self(); } - public SELF $_tests_fail( int nFailedTests ) { - assertThat( result.getFailureCount() ).isEqualTo( nFailedTests ); + public SELF $_tests_fail(int nFailedTests) { + assertThat(result.getFailureCount()).isEqualTo(nFailedTests); return self(); } public SELF the_test_fails() { - assertThat( result.getFailureCount() ).as( "failure count" ).isGreaterThan( 0 ); + assertThat(result.getFailureCount()).as("failure count").isGreaterThan(0); return self(); } - public void the_test_fails_with_message( String expectedMessage ) { + public SELF the_test_fails_with_message(String expectedMessage) { the_test_fails(); - assertThat( result.getFailureMessage( 0 ) ).as( "failure message" ).isEqualTo( expectedMessage ); + assertThat(result.getFailureMessage(0)).as("failure message").contains(expectedMessage); + return self(); } public SELF the_report_model_contains_one_scenario_for_each_test_method() { Method[] declaredMethods = testScenario.testClass.getDeclaredMethods(); - List nonStaticMethods = ReflectionUtil.getNonStaticMethod( testScenario.testClass.getDeclaredMethods() ); - assertThat( reportModel.getScenarios() ).hasSize( nonStaticMethods.size() ); + List nonStaticMethods = ReflectionUtil.getNonStaticMethod(testScenario.testClass.getDeclaredMethods()); + assertThat(reportModel.getScenarios()).hasSize(nonStaticMethods.size()); return self(); } - public SELF each_scenario_contains_$_cases( int nParameters ) { - for( ScenarioModel scenario : reportModel.getScenarios() ) { - assertThat( scenario.getScenarioCases() ).hasSize( nParameters ); + public SELF each_scenario_contains_$_cases(int nParameters) { + for (ScenarioModel scenario : reportModel.getScenarios()) { + assertThat(scenario.getScenarioCases()).hasSize(nParameters); } return self(); } public SELF has_a_valid_class_name_if_it_is_not_null() { - if( reportModel != null ) { + if (reportModel != null) { the_report_model_has_a_valid_class_name(); } return self(); } public SELF the_report_model_has_a_valid_class_name() { - assertThat( reportModel.getClassName() ).isEqualTo( testScenario.testClass.getName() ); + assertThat(reportModel.getClassName()).isEqualTo(testScenario.testClass.getName()); return self(); } - public SELF the_scenario_has_execution_status( ExecutionStatus status ) { - assertThat( reportModel.getLastScenarioModel().getExecutionStatus() ).isEqualTo( status ); + public SELF the_scenario_has_execution_status(ExecutionStatus status) { + assertThat(reportModel.getLastScenarioModel().getExecutionStatus()).isEqualTo(status); return self(); } } diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/WhenTestFramework.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/WhenTestFramework.java index d7652cf071..9d608d7624 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/WhenTestFramework.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/testframework/WhenTestFramework.java @@ -22,34 +22,42 @@ public class WhenTestFramework> extends Stage< @ProvidedScenarioState private TestExecutionResult testExecutionResult; - public SELF the_test_is_executed_with( TestFramework framework ) { - Assertions.assertThat( testScenario ).as( "No matching test scenario found" ).isNotNull(); + public SELF the_test_is_executed_with(TestFramework framework) { + Assertions.assertThat(testScenario).as("No matching test scenario found").isNotNull(); - executor = TestExecutor.getExecutor( framework ); - testExecutionResult = executor.execute( testScenario.testClass, testScenario.testMethod ); + executor = TestExecutor.getExecutor(framework); + testExecutionResult = executor.execute(testScenario.testClass, testScenario.testMethod); reportModel = testExecutionResult.getReportModel(); return self(); } - public SELF the_test_class_is_executed_with( TestFramework framework ) { - Assertions.assertThat( testScenario ).as( "No matching test scenario found" ).isNotNull(); + public SELF the_test_class_is_executed_with(TestFramework framework) { + Assertions.assertThat(testScenario).as("No matching test scenario found").isNotNull(); - executor = TestExecutor.getExecutor( framework ); - testExecutionResult = executor.execute( testScenario.testClass ); + executor = TestExecutor.getExecutor(framework); + testExecutionResult = executor.execute(testScenario.testClass); reportModel = testExecutionResult.getReportModel(); return self(); } public SELF the_test_is_executed_with_JUnit() { - return the_test_is_executed_with( TestFramework.JUnit ); + return the_test_is_executed_with(TestFramework.JUnit); + } + + public SELF the_test_is_executed_with_JUnit5() { + return the_test_is_executed_with(TestFramework.JUnit5); } public SELF the_test_is_executed_with_TestNG() { - return the_test_is_executed_with( TestFramework.TestNG ); + return the_test_is_executed_with(TestFramework.TestNG); } public SELF the_test_class_is_executed_with_JUnit() { - return the_test_class_is_executed_with( TestFramework.JUnit ); + return the_test_class_is_executed_with(TestFramework.JUnit); + } + + public SELF the_test_class_is_executed_with_JUnit5() { + return the_test_class_is_executed_with(TestFramework.JUnit5); } } diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/testng/TestNgExecutor.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/testng/TestNgExecutor.java index b9af087ce7..4b579133cc 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/testng/TestNgExecutor.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/testng/TestNgExecutor.java @@ -37,7 +37,7 @@ public TestExecutionResult execute( Class testClass, String testMethod ) { } @Override - public TestExecutionResult execute( Class testClass ) { + public TestExecutionResult execute(Class testClass ) { return execute( testClass, null ); }