diff --git a/build.gradle b/build.gradle index 71abcf2cdf6..d7ed1cad001 100644 --- a/build.gradle +++ b/build.gradle @@ -182,11 +182,18 @@ configure(subprojects) { } +configure(subprojects.findAll {!it.name.contains("jgiven-junit5")}) { + apply plugin: 'ru.vyarus.animalsniffer' + + dependencies { + signature 'org.codehaus.mojo.signature:java16:1.1@signature' + } +} + configure(subprojects.findAll {!it.name.contains("android")}) { apply plugin: 'checkstyle' apply plugin: 'java' apply plugin: 'org.asciidoctor.convert' - apply plugin: 'ru.vyarus.animalsniffer' dependencies { compile group: 'org.slf4j', name: 'slf4j-api', version: slf4jVersion @@ -197,7 +204,6 @@ configure(subprojects.findAll {!it.name.contains("android")}) { testCompile group: 'org.assertj', name: 'assertj-core', version: assertjVersion testCompile group: 'com.tngtech.java', name: 'junit-dataprovider', version: junitDataproviderVersion testCompile group: 'net.java.quickcheck', name: 'quickcheck', version: quickcheckVersion - signature 'org.codehaus.mojo.signature:java16:1.1@signature' } sourceCompatibility = targetCompatibility = 1.6 diff --git a/docs/index.adoc b/docs/index.adoc index dd74aa473f1..a3fd9863ca7 100644 --- a/docs/index.adoc +++ b/docs/index.adoc @@ -41,3 +41,5 @@ include::spring.adoc[] include::selenium.adoc[] include::android.adoc[] + +include::junit5.adoc[] diff --git a/docs/intro.adoc b/docs/intro.adoc index 7d71d296c1d..cc02e44744e 100644 --- a/docs/intro.adoc +++ b/docs/intro.adoc @@ -28,4 +28,8 @@ stages classes can be treated as Spring beans. ==== JGiven Android Provides support for executing JGiven tests on an Android device or emulator. +This is currently in an experimental status. + +==== JGiven JUnit 5 +This module provides an integration into JUnit 5.x. This is currently in an experimental status. \ No newline at end of file diff --git a/docs/junit5.adoc b/docs/junit5.adoc new file mode 100644 index 00000000000..020852a45f8 --- /dev/null +++ b/docs/junit5.adoc @@ -0,0 +1,86 @@ +== JUnit 5 (EXPERIMENTAL) +:javadocurl: http://static.javadoc.io/com.tngtech.jgiven/jgiven-junit5/{version}/com/tngtech/jgiven/junit5 + +CAUTION: JUnit 5 support is currently in an experimental state. +This means that the API is not completely stable yet and that not all +JGiven and JUnit 5 features are completely supported yet. + +=== Known Issues + +* Nested tests are all reported under the outer parent test class +* Dynamic tests are not reported at all. As dynamic tests in JUnit 5 do not provide life-cycle + hooks, it is unclear at the moment, whether JGiven will ever support them. + +=== Install Dependency +JUnit 5 support is provided by the `jgiven-junit5` dependency. + +==== Maven + +[source,maven,subs="verbatim,attributes"] +---- + + com.tngtech.jgiven + jgiven-junit5 + {version} + test + +---- + +==== Gradle + +[source,gradle,subs="verbatim,attributes"] +---- +dependencies { + testCompile("com.tngtech.jgiven:jgiven-junit5:{version}") +} +---- + +=== Use JGiven with JUnit 5 + +JGiven support for JUnit 5 is provided by the link:{javadocurl}/JGivenExtension.html[`JGivenExtension`] +JUnit 5 extension. You just annotate your JUnit 5 test class with `@ExtendWith( JGivenExtension.class )` +and JGiven is enabled. + +If you just use the extension directly, you have to inject your stage classes by using +the `@ScenarioStage` annotation. + +[source,java] +---- +@ExtendWith( JGivenExtension.class ) +public class JUnit5Test { + + @ScenarioStage + MyStage myStage; + + @Test + public void my_scenario() { + myStage + .given().some_context() + .when().some_action() + .then().some_outcome(); + + } +} +---- + +Alternatively, you can use one of the test base classes link:{javadocurl}/ScenarioTest.html[`ScenarioTest`] or +link:{javadocurl}/SimpleScenarioTest.html[`SimpleScenarioTest`] that provide additional convenience methods +and allows you to specify stage classes by using type parameters: + +[source,java] +---- +public class JGiven5ScenarioTest + extends ScenarioTest { + + @Test + public void JGiven_works_with_JUnit5() { + given().some_state(); + when().some_action(); + then().some_outcome(); + } +} +---- + +=== Example Project + +You find a complete example project on GitHub: https://github.com/TNG/JGiven/tree/master/example-projects/junit5 diff --git a/example-projects/junit5/README.md b/example-projects/junit5/README.md new file mode 100644 index 00000000000..711667c51c2 --- /dev/null +++ b/example-projects/junit5/README.md @@ -0,0 +1,6 @@ +# JUnit 5 Example Project + +This project shows how JGiven can be used with JUnit 5 + +1. Run `gradle build` +2. Open `build/reports/jgiven/test/html/index.html` diff --git a/example-projects/junit5/build.gradle b/example-projects/junit5/build.gradle new file mode 100644 index 00000000000..fc4709005ea --- /dev/null +++ b/example-projects/junit5/build.gradle @@ -0,0 +1,51 @@ + +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0-M3' + } +} + +plugins { + id "com.tngtech.jgiven.gradle-plugin" version "0.13.0" +} + +apply plugin: 'org.junit.platform.gradle.plugin' + +apply plugin: 'java' +apply plugin: 'idea' +apply plugin: 'eclipse' + +compileJava { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + options.compilerArgs += '-parameters' + options.encoding = 'UTF-8' +} + +ext { + junit5Version = '5.0.0-M3' +} + +repositories { + mavenLocal() + mavenCentral() + maven { + url "https://oss.sonatype.org/content/repositories/snapshots/" + } +} + +dependencies { + testCompile 'com.tngtech.jgiven:jgiven-junit5:latest.integration' + testCompile 'org.junit.jupiter:junit-jupiter-api:' + junit5Version + testCompile 'org.junit.jupiter:junit-jupiter-engine:' + junit5Version +} + +junitPlatform { + enableStandardTestTask true + platformVersion = "1.0.0-M3" +} + +test.finalizedBy jgivenTestReport diff --git a/example-projects/junit5/src/test/java/com/tngtech/jgiven/example/projects/junit5/GivenStage.java b/example-projects/junit5/src/test/java/com/tngtech/jgiven/example/projects/junit5/GivenStage.java new file mode 100644 index 00000000000..1b5b3993d30 --- /dev/null +++ b/example-projects/junit5/src/test/java/com/tngtech/jgiven/example/projects/junit5/GivenStage.java @@ -0,0 +1,16 @@ +package com.tngtech.jgiven.example.projects.junit5; + +import com.tngtech.jgiven.Stage; +import com.tngtech.jgiven.annotation.Quoted; +import com.tngtech.jgiven.annotation.ScenarioState; + +public class GivenStage extends Stage { + + @ScenarioState + String message; + + GivenStage message(@Quoted String message) { + this.message = message; + return self(); + } +} diff --git a/example-projects/junit5/src/test/java/com/tngtech/jgiven/example/projects/junit5/JUnit5Test.java b/example-projects/junit5/src/test/java/com/tngtech/jgiven/example/projects/junit5/JUnit5Test.java new file mode 100644 index 00000000000..dc1f2b9df5b --- /dev/null +++ b/example-projects/junit5/src/test/java/com/tngtech/jgiven/example/projects/junit5/JUnit5Test.java @@ -0,0 +1,27 @@ +package com.tngtech.jgiven.example.projects.junit5; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import com.tngtech.jgiven.annotation.ScenarioStage; +import com.tngtech.jgiven.junit5.JGivenExtension; + +@ExtendWith( JGivenExtension.class ) +public class JUnit5Test { + + @ScenarioStage + GivenStage givenStage; + + @ScenarioStage + WhenStage whenStage; + + @ScenarioStage + ThenStage thenStage; + + @Test + public void scenario_with_JUnit5() { + givenStage.given().message( "Hello JUnit" ); + whenStage.when().handle_message(); + thenStage.then().the_result_is( "Hello JUnit 5!" ); + } +} \ No newline at end of file diff --git a/example-projects/junit5/src/test/java/com/tngtech/jgiven/example/projects/junit5/ThenStage.java b/example-projects/junit5/src/test/java/com/tngtech/jgiven/example/projects/junit5/ThenStage.java new file mode 100644 index 00000000000..7f9eac18dc7 --- /dev/null +++ b/example-projects/junit5/src/test/java/com/tngtech/jgiven/example/projects/junit5/ThenStage.java @@ -0,0 +1,14 @@ +package com.tngtech.jgiven.example.projects.junit5; + +import com.tngtech.jgiven.Stage; +import com.tngtech.jgiven.annotation.ScenarioState; +import org.junit.jupiter.api.Assertions; + +public class ThenStage extends Stage { + @ScenarioState(required = true) + String result; + + public void the_result_is(String expectedResult) { + Assertions.assertEquals(expectedResult, result); + } +} diff --git a/example-projects/junit5/src/test/java/com/tngtech/jgiven/example/projects/junit5/WhenStage.java b/example-projects/junit5/src/test/java/com/tngtech/jgiven/example/projects/junit5/WhenStage.java new file mode 100644 index 00000000000..8fb194013ff --- /dev/null +++ b/example-projects/junit5/src/test/java/com/tngtech/jgiven/example/projects/junit5/WhenStage.java @@ -0,0 +1,16 @@ +package com.tngtech.jgiven.example.projects.junit5; + +import com.tngtech.jgiven.Stage; +import com.tngtech.jgiven.annotation.ScenarioState; + +public class WhenStage extends Stage { + @ScenarioState(required = true) + String message; + + @ScenarioState + String result; + + public void handle_message() { + result = message + " 5!"; + } +} diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/config/ConfigurationUtil.java b/jgiven-core/src/main/java/com/tngtech/jgiven/config/ConfigurationUtil.java index e181960288f..97da2af735a 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/config/ConfigurationUtil.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/config/ConfigurationUtil.java @@ -23,12 +23,15 @@ public AbstractJGivenConfiguration load( Class key ) throws Exception { public static AbstractJGivenConfiguration getConfiguration( Class testClass ) { JGivenConfiguration annotation = testClass.getAnnotation( JGivenConfiguration.class ); + Class configuration; if( annotation == null ) { - return new DefaultConfiguration(); + configuration = DefaultConfiguration.class; + } else { + configuration = annotation.value(); } try { - return configurations.get( annotation.value() ); + return configurations.get( configuration ); } catch( ExecutionException e ) { throw Throwables.propagate( e.getCause() ); } diff --git a/jgiven-testng/src/main/java/com/tngtech/jgiven/testng/ScenarioHolder.java b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/ScenarioHolder.java similarity index 87% rename from jgiven-testng/src/main/java/com/tngtech/jgiven/testng/ScenarioHolder.java rename to jgiven-core/src/main/java/com/tngtech/jgiven/impl/ScenarioHolder.java index 62488d553ed..e984211b7cd 100644 --- a/jgiven-testng/src/main/java/com/tngtech/jgiven/testng/ScenarioHolder.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/ScenarioHolder.java @@ -1,6 +1,4 @@ -package com.tngtech.jgiven.testng; - -import com.tngtech.jgiven.impl.ScenarioBase; +package com.tngtech.jgiven.impl; public class ScenarioHolder { private final ThreadLocal scenario = new ThreadLocal(); diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/ScenarioModelBuilder.java b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/ScenarioModelBuilder.java index 2707f887a23..dfcaf48578a 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/ScenarioModelBuilder.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/ScenarioModelBuilder.java @@ -324,6 +324,7 @@ public void scenarioFailed( Throwable e ) { public void scenarioStarted( Class testClass, Method method, List namedArguments ) { readConfiguration( testClass ); readAnnotations( testClass, method ); + scenarioModel.setClassName(testClass.getName()); setParameterNames( getNames( namedArguments ) ); // must come at last diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java index 1881f958215..66c3e392689 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java @@ -5,6 +5,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import com.tngtech.jgiven.impl.util.ReflectionUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/util/ReflectionUtil.java b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/util/ReflectionUtil.java index b2fc5915eed..7fe64fe33d1 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/util/ReflectionUtil.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/util/ReflectionUtil.java @@ -208,18 +208,22 @@ private static Function getFieldValueFunction( final Object targe return new Function() { @Override public Object apply( Field field ) { - makeAccessible( field, "" ); - try { - return field.get( target ); - } catch( IllegalAccessException e ) { - log.warn( - format( "Not able to access field '%s'." + errorDescription, toReadableString( field ) ), e ); - return null; - } + return getFieldValueOrNull(field, target, errorDescription); } }; } + public static Object getFieldValueOrNull(Field field, Object target, String errorDescription) { + makeAccessible( field, "" ); + try { + return field.get( target ); + } catch( IllegalAccessException e ) { + log.warn( + format( "Not able to access field '%s'." + errorDescription, toReadableString( field ) ), e ); + return null; + } + } + public static List getAllNonStaticFields( Class clazz ) { final List result = Lists.newArrayList(); diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ReportModel.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ReportModel.java index a740971c6e0..02f9ed76e8d 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ReportModel.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/model/ReportModel.java @@ -161,7 +161,6 @@ public synchronized void setTagMap( Map tagMap ) { } public synchronized void addScenarioModelOrMergeWithExistingOne( ScenarioModel scenarioModel ) { - scenarioModel.setClassName( getClassName() ); Optional existingScenarioModel = findScenarioModel( scenarioModel.getDescription() ); if( existingScenarioModel.isPresent() ) { @@ -192,4 +191,8 @@ public synchronized void setTestClass( Class testClass ) { public String getName() { return name; } + + public void setName( String name ) { + this.name = name; + } } diff --git a/jgiven-junit5/build.gradle b/jgiven-junit5/build.gradle new file mode 100644 index 00000000000..bca3ec94102 --- /dev/null +++ b/jgiven-junit5/build.gradle @@ -0,0 +1,43 @@ +description = "Module for writing JGiven tests with JUnit 5" + +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0-M3' + } +} + +apply plugin: 'org.junit.platform.gradle.plugin' + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +compileJava { + options.compilerArgs += '-parameters' + options.encoding = 'UTF-8' +} + +junitPlatform { + enableStandardTestTask true +} + +afterEvaluate { + tasks.getByName('junitPlatformTest') { + systemProperty 'jgiven.report.dir', 'build/reports/jgiven/json' + systemProperty 'jgiven.report.text', 'false' + systemProperty 'org.slf4j.simpleLogger.defaultLogLevel', 'warn' + finalizedBy(jgivenHtml5Report) + } +} + +dependencies { + compile project(':jgiven-core') + + compileOnly 'org.junit.jupiter:junit-jupiter-api:5.0.0-M3' + + testCompile project(':jgiven-html5-report') + testCompile 'org.junit.jupiter:junit-jupiter-engine:5.0.0-M3' + testCompile 'org.junit.platform:junit-platform-runner:1.0.0-M3' +} 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 new file mode 100644 index 00000000000..a0c0a26ba16 --- /dev/null +++ b/jgiven-junit5/src/main/java/com/tngtech/jgiven/junit5/JGivenExtension.java @@ -0,0 +1,130 @@ +package com.tngtech.jgiven.junit5; + +import static com.tngtech.jgiven.report.model.ExecutionStatus.FAILED; +import static com.tngtech.jgiven.report.model.ExecutionStatus.SUCCESS; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; + +import com.tngtech.jgiven.config.ConfigurationUtil; +import com.tngtech.jgiven.impl.Config; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ContainerExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.TestExtensionContext; +import org.junit.jupiter.api.extension.TestInstancePostProcessor; +import org.junit.jupiter.api.extension.ExtensionContext.Namespace; + +import com.tngtech.jgiven.base.ScenarioTestBase; +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.NamedArgument; +import com.tngtech.jgiven.report.model.ReportModel; + +/** + * This extension enables JGiven for JUnit 5 Tests. + *

+ * Just annotate your test class with {@code @ExtendWith( JGivenExtension.class )} + *

+ * You can then inject stage classes by using the {@see ScenarioStage} annotation + *

+ * As an alternative you can also inherit from one of the following predefined base classes: + *

    + *
  • {@link ScenarioTest}
  • + *
  • {@link SimpleScenarioTest}
  • + *
+ * + * @see ScenarioTest + * @see SimpleScenarioTest + */ +public class JGivenExtension implements + TestInstancePostProcessor, + BeforeAllCallback, + AfterAllCallback, + BeforeEachCallback, + AfterEachCallback { + + private static final Namespace NAMESPACE = Namespace.create( "com.tngtech.jgiven" ); + + private static final String REPORT_MODEL = "report-model"; + + @Override + public void beforeAll( ContainerExtensionContext context ) throws Exception { + ReportModel reportModel = new ReportModel(); + reportModel.setTestClass( context.getTestClass().get() ); + if( !context.getDisplayName().equals( context.getTestClass().get().getSimpleName() ) ) { + reportModel.setName( context.getDisplayName() ); + } + context.getStore( NAMESPACE ).put( REPORT_MODEL, reportModel ); + + ConfigurationUtil.getConfiguration(context.getTestClass().get()) + .configureTag(Tag.class) + .description("JUnit 5 Tag") + .color("orange"); + } + + @Override + public void afterAll( ContainerExtensionContext context ) throws Exception { + new CommonReportHelper().finishReport( (ReportModel) context.getStore( NAMESPACE ).get( REPORT_MODEL ) ); + } + + @Override + public void beforeEach( TestExtensionContext context ) throws Exception { + List args = new ArrayList(); + getScenario().startScenario( context.getTestClass().get(), context.getTestMethod().get(), args ); + + } + + @Override + public void afterEach( TestExtensionContext context ) throws Exception { + + ScenarioBase scenario = getScenario(); + try { + if( context.getTestException().isPresent() ) { + scenario.getExecutor().failed( context.getTestException().get() ); + } + scenario.finished(); + + // ignore test when scenario is not implemented + Assumptions.assumeTrue( EnumSet.of( SUCCESS, FAILED ).contains( scenario.getScenarioModel().getExecutionStatus() ) ); + + } catch( Exception e ) { + throw e; + } catch( Throwable e ) { + throw new RuntimeException( e ); + } finally { + ScenarioHolder.get().removeScenarioOfCurrentThread(); + } + + } + + private ScenarioBase getScenario() { + return ScenarioHolder.get().getScenarioOfCurrentThread(); + } + + @Override + public void postProcessTestInstance( Object testInstance, ExtensionContext context ) throws Exception { + ScenarioBase scenario = ScenarioHolder.get().getScenarioOfCurrentThread(); + + if( scenario == null ) { + if( testInstance instanceof ScenarioTestBase ) { + scenario = ( (ScenarioTestBase) testInstance ).getScenario(); + } else { + scenario = new ScenarioBase(); + } + ReportModel reportModel = (ReportModel) context.getStore( NAMESPACE ).get( REPORT_MODEL ); + scenario.setModel( reportModel ); + ScenarioHolder.get().setScenarioOfCurrentThread( scenario ); + } + + scenario.getExecutor().injectStages( testInstance ); + scenario.getExecutor().readScenarioState( testInstance ); + } +} diff --git a/jgiven-junit5/src/main/java/com/tngtech/jgiven/junit5/ScenarioTest.java b/jgiven-junit5/src/main/java/com/tngtech/jgiven/junit5/ScenarioTest.java new file mode 100644 index 00000000000..10e497ea843 --- /dev/null +++ b/jgiven-junit5/src/main/java/com/tngtech/jgiven/junit5/ScenarioTest.java @@ -0,0 +1,30 @@ +package com.tngtech.jgiven.junit5; + +import com.tngtech.jgiven.base.ScenarioTestBase; +import com.tngtech.jgiven.impl.Scenario; +import org.junit.jupiter.api.extension.ExtendWith; + + +/** + * Convenience test base class for writing JGiven scenarios with JUnit 5. + * If you only have one stage class you can also use the {@link SimpleScenarioTest} class. + * If you don't want to inherit from any class you can just use the {@link JGivenExtension} + * directly. + * + * @param the GIVEN stage + * @param the WHEN stage + * @param the THEN stage + * + * @see JGivenExtension + * @see SimpleScenarioTest + */ +@ExtendWith( JGivenExtension.class ) +public class ScenarioTest extends ScenarioTestBase { + + private Scenario scenario = createScenario(); + + @Override + public Scenario getScenario() { + return scenario; + } +} diff --git a/jgiven-junit5/src/main/java/com/tngtech/jgiven/junit5/SimpleScenarioTest.java b/jgiven-junit5/src/main/java/com/tngtech/jgiven/junit5/SimpleScenarioTest.java new file mode 100644 index 00000000000..9eea7abfd2c --- /dev/null +++ b/jgiven-junit5/src/main/java/com/tngtech/jgiven/junit5/SimpleScenarioTest.java @@ -0,0 +1,19 @@ +package com.tngtech.jgiven.junit5; + +import com.tngtech.jgiven.base.SimpleScenarioTestBase; +import org.junit.jupiter.api.extension.ExtendWith; + +import com.tngtech.jgiven.base.ScenarioTestBase; +import com.tngtech.jgiven.impl.Scenario; + + +@ExtendWith( JGivenExtension.class ) +public class SimpleScenarioTest extends SimpleScenarioTestBase { + + private Scenario scenario = createScenario(); + + @Override + public Scenario getScenario() { + return scenario; + } +} diff --git a/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/DynamicTestTest.java b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/DynamicTestTest.java new file mode 100644 index 00000000000..8c50ef75e40 --- /dev/null +++ b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/DynamicTestTest.java @@ -0,0 +1,31 @@ +package com.tngtech.jgiven.junit5.test; + +import com.tngtech.jgiven.junit5.JGivenExtension; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; + +@ExtendWith(JGivenExtension.class) +@RunWith( JUnitPlatform.class ) +@DisplayName("Dynamic Tests") +public class DynamicTestTest { + + @TestFactory + Collection dynamicTestsFromCollection() { + return Arrays.asList( + dynamicTest("1st dynamic test", () -> assertTrue(true)), + dynamicTest("2nd dynamic test", () -> assertEquals(4, 2 * 2)) + ); + } + +} diff --git a/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/GeneralStage.java b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/GeneralStage.java new file mode 100644 index 00000000000..691f4c7b948 --- /dev/null +++ b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/GeneralStage.java @@ -0,0 +1,21 @@ +package com.tngtech.jgiven.junit5.test; + +import org.junit.jupiter.api.Assertions; + +public class GeneralStage { + + private String someState; + + public void some_state() { + someState = "SomeState"; + + } + + public void some_action() { + Assertions.assertNotNull(someState); + } + + public void some_outcome() { + Assertions.assertNotNull(someState); + } +} diff --git a/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/GivenStage.java b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/GivenStage.java new file mode 100644 index 00000000000..b49b2e754a3 --- /dev/null +++ b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/GivenStage.java @@ -0,0 +1,15 @@ +package com.tngtech.jgiven.junit5.test; + +import com.tngtech.jgiven.annotation.ExpectedScenarioState; +import com.tngtech.jgiven.annotation.ProvidedScenarioState; + +public class GivenStage { + + @ProvidedScenarioState + String someState; + + public void some_state() { + someState = "Some State"; + } + +} diff --git a/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/JGiven5ExtensionTest.java b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/JGiven5ExtensionTest.java new file mode 100644 index 00000000000..fbd2d95e4d6 --- /dev/null +++ b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/JGiven5ExtensionTest.java @@ -0,0 +1,37 @@ +package com.tngtech.jgiven.junit5.test; + +import com.tngtech.jgiven.annotation.Pending; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import com.tngtech.jgiven.Stage; +import com.tngtech.jgiven.annotation.ScenarioStage; +import com.tngtech.jgiven.junit5.JGivenExtension; + +@ExtendWith( JGivenExtension.class ) +public class JGiven5ExtensionTest { + + @ScenarioStage + GivenStage givenStage; + + @ScenarioStage + WhenStage whenStage; + + @ScenarioStage + ThenStage thenStage; + + @Test + public void JGiven_works_with_JUnit5() { + givenStage.some_state(); + whenStage.some_action(); + thenStage.some_outcome(); + } + + @Test + @Pending + public void Pending_works() { + whenStage.some_failing_step(); + } + +} diff --git a/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/JGiven5ScenarioTest.java b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/JGiven5ScenarioTest.java new file mode 100644 index 00000000000..2853acf3bd4 --- /dev/null +++ b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/JGiven5ScenarioTest.java @@ -0,0 +1,19 @@ +package com.tngtech.jgiven.junit5.test; + +import com.tngtech.jgiven.junit5.ScenarioTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import com.tngtech.jgiven.annotation.ScenarioStage; +import com.tngtech.jgiven.junit5.JGivenExtension; + +public class JGiven5ScenarioTest extends ScenarioTest { + + @Test + public void JGiven_works_with_JUnit5() { + given().some_state(); + when().some_action(); + then().some_outcome(); + } + +} diff --git a/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/JGiven5SimpleScenarioTest.java b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/JGiven5SimpleScenarioTest.java new file mode 100644 index 00000000000..5545f11d9d6 --- /dev/null +++ b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/JGiven5SimpleScenarioTest.java @@ -0,0 +1,15 @@ +package com.tngtech.jgiven.junit5.test; + +import com.tngtech.jgiven.junit5.SimpleScenarioTest; +import org.junit.jupiter.api.Test; + +public class JGiven5SimpleScenarioTest extends SimpleScenarioTest { + + @Test + public void JGiven_works_with_JUnit5() { + given().some_state(); + when().some_action(); + then().some_outcome(); + } + +} diff --git a/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/NestedTest.java b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/NestedTest.java new file mode 100644 index 00000000000..bf15062de37 --- /dev/null +++ b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/NestedTest.java @@ -0,0 +1,100 @@ +package com.tngtech.jgiven.junit5.test; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +import com.tngtech.jgiven.annotation.BeforeStage; +import com.tngtech.jgiven.annotation.ExpectedScenarioState; +import com.tngtech.jgiven.annotation.ScenarioStage; +import com.tngtech.jgiven.annotation.ScenarioState; +import com.tngtech.jgiven.junit5.JGivenExtension; + +@RunWith( JUnitPlatform.class ) +@ExtendWith( { JGivenExtension.class } ) +@DisplayName( "@Nested") +public class NestedTest { + + @ScenarioStage + GeneralStage outerStage; + + @ScenarioState + String outerState = "Outer State"; + + @Nested + @DisplayName("1st level nesting") + class NestedTestClass { + + @ScenarioStage + NestedStage stage; + + @ScenarioState + String nestedState = "Nested State"; + + @BeforeEach + public void background() { + stage.some_background(); + } + + @Test + public void nested_classes() { + assertThat( outerStage ).as( "outerStage" ).isNotNull(); + assertThat( stage ).as( "stage" ).isNotNull(); + + outerStage.some_state(); + stage.some_action(); + stage.some_outcome(); + } + + @Test + public void another_test() { + + } + + @Nested + @DisplayName("2nd level nesting") + class NestedDeeper { + @Test + public void deeply_nested_classes() { + assertThat( outerStage ).as( "outerStage" ).isNotNull(); + assertThat( stage ).as( "stage" ).isNotNull(); + + outerStage.some_state(); + stage.some_action(); + stage.some_outcome(); + } + + } + } + + static class NestedStage { + @ExpectedScenarioState + String outerState; + + @ExpectedScenarioState + String nestedState; + + @BeforeStage + void setup() { + assertThat( outerState ).as( "outerState" ).isNotNull(); + assertThat( nestedState ).as( "nestedState" ).isNotNull(); + } + + public void some_action() { + + } + + public void some_outcome() {} + + public void some_background() { + + } + } + +} diff --git a/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/TagTest.java b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/TagTest.java new file mode 100644 index 00000000000..47075689814 --- /dev/null +++ b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/TagTest.java @@ -0,0 +1,39 @@ +package com.tngtech.jgiven.junit5.test; + +import com.tngtech.jgiven.annotation.JGivenConfiguration; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import com.tngtech.jgiven.annotation.Pending; +import com.tngtech.jgiven.annotation.ScenarioStage; +import com.tngtech.jgiven.junit5.JGivenExtension; + +@Tag("Class Tag") +@ExtendWith( JGivenExtension.class ) +public class TagTest { + + @ScenarioStage + GivenStage givenStage; + + @ScenarioStage + WhenStage whenStage; + + @ScenarioStage + ThenStage thenStage; + + @Test + @Tag("Method Tag") + public void scenario_with_a_JUnit5_tag() { + givenStage.some_state(); + whenStage.some_action(); + thenStage.some_outcome(); + } + + @Test + @Pending + public void Pending_works() { + whenStage.some_failing_step(); + } + +} diff --git a/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/ThenStage.java b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/ThenStage.java new file mode 100644 index 00000000000..7f264518247 --- /dev/null +++ b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/ThenStage.java @@ -0,0 +1,18 @@ +package com.tngtech.jgiven.junit5.test; + +import com.tngtech.jgiven.annotation.ExpectedScenarioState; +import com.tngtech.jgiven.annotation.ProvidedScenarioState; +import org.junit.jupiter.api.Assertions; + +public class ThenStage { + @ExpectedScenarioState + String someState; + + @ExpectedScenarioState + String someResult; + + void some_outcome() { + Assertions.assertNotNull(someState); + Assertions.assertNotNull(someResult); + } +} diff --git a/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/WhenStage.java b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/WhenStage.java new file mode 100644 index 00000000000..851e3faf2c6 --- /dev/null +++ b/jgiven-junit5/src/test/java/com/tngtech/jgiven/junit5/test/WhenStage.java @@ -0,0 +1,23 @@ +package com.tngtech.jgiven.junit5.test; + +import com.tngtech.jgiven.annotation.ExpectedScenarioState; +import com.tngtech.jgiven.annotation.ProvidedScenarioState; +import org.junit.jupiter.api.Assertions; + +public class WhenStage { + + @ExpectedScenarioState + String someState; + + @ProvidedScenarioState + String someResult; + + void some_action() { + Assertions.assertNotNull(someState); + someResult = "Some Result"; + } + + public void some_failing_step() { + Assertions.assertTrue(false, "Intentionally failing"); + } +} diff --git a/jgiven-testng/build.gradle b/jgiven-testng/build.gradle index c798a518f06..d91f9624707 100644 --- a/jgiven-testng/build.gradle +++ b/jgiven-testng/build.gradle @@ -5,11 +5,6 @@ dependencies { compile project(':jgiven-html5-report') compile(group: 'org.testng', name: 'testng', version: '6.9.12') testCompile(group: 'org.testng.testng-remote', name: 'testng-remote-dist', version: '1.0.0') - - /* This dependency was originally in the Maven provided scope, but the project was not of type war. - This behavior is not yet supported by Gradle, so this dependency has been converted to a compile dependency. - Please review and delete this closure when resolved. */ - } test.useTestNG() { diff --git a/jgiven-testng/src/main/java/com/tngtech/jgiven/testng/ScenarioTest.java b/jgiven-testng/src/main/java/com/tngtech/jgiven/testng/ScenarioTest.java index 05446e11445..180458b1978 100644 --- a/jgiven-testng/src/main/java/com/tngtech/jgiven/testng/ScenarioTest.java +++ b/jgiven-testng/src/main/java/com/tngtech/jgiven/testng/ScenarioTest.java @@ -1,5 +1,6 @@ package com.tngtech.jgiven.testng; +import com.tngtech.jgiven.impl.ScenarioHolder; import org.testng.annotations.Listeners; import com.tngtech.jgiven.base.ScenarioTestBase; diff --git a/jgiven-testng/src/main/java/com/tngtech/jgiven/testng/ScenarioTestListener.java b/jgiven-testng/src/main/java/com/tngtech/jgiven/testng/ScenarioTestListener.java index bcafd0d0dbe..4f1180e026c 100644 --- a/jgiven-testng/src/main/java/com/tngtech/jgiven/testng/ScenarioTestListener.java +++ b/jgiven-testng/src/main/java/com/tngtech/jgiven/testng/ScenarioTestListener.java @@ -7,6 +7,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import com.tngtech.jgiven.impl.ScenarioHolder; import org.testng.ITestContext; import org.testng.ITestListener; import org.testng.ITestResult; diff --git a/jgiven-testng/src/main/java/com/tngtech/jgiven/testng/SimpleScenarioTest.java b/jgiven-testng/src/main/java/com/tngtech/jgiven/testng/SimpleScenarioTest.java index 0f38fa31f72..a4f0e124363 100644 --- a/jgiven-testng/src/main/java/com/tngtech/jgiven/testng/SimpleScenarioTest.java +++ b/jgiven-testng/src/main/java/com/tngtech/jgiven/testng/SimpleScenarioTest.java @@ -1,6 +1,7 @@ package com.tngtech.jgiven.testng; import com.tngtech.jgiven.impl.Scenario; +import com.tngtech.jgiven.impl.ScenarioHolder; import org.testng.annotations.Listeners; import com.tngtech.jgiven.base.SimpleScenarioTestBase; 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 8643e19123d..484a47a3b02 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 @@ -2,7 +2,7 @@ import com.tngtech.jgiven.impl.Scenario; import com.tngtech.jgiven.junit.ScenarioTest; -import com.tngtech.jgiven.testng.ScenarioHolder; +import com.tngtech.jgiven.impl.ScenarioHolder; public class ScenarioTestForTesting extends ScenarioTest { @Override diff --git a/settings.gradle b/settings.gradle index f02604c2eb0..b1d3772cbdd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -22,6 +22,10 @@ if (JavaVersion.current().isJava8Compatible() && !release) { include ':jgiven-java8-tests' } +if (JavaVersion.current().isJava8Compatible()) { + include ':jgiven-junit5' +} + if (android || release) { include ':jgiven-android' }