diff --git a/src/main/java/com/greghaskins/spectrum/dsl/gherkin/Gherkin.java b/src/main/java/com/greghaskins/spectrum/dsl/gherkin/Gherkin.java index 1f84b39..a3d1b1c 100644 --- a/src/main/java/com/greghaskins/spectrum/dsl/gherkin/Gherkin.java +++ b/src/main/java/com/greghaskins/spectrum/dsl/gherkin/Gherkin.java @@ -2,6 +2,7 @@ import static com.greghaskins.spectrum.dsl.specification.Specification.describe; import static com.greghaskins.spectrum.dsl.specification.Specification.it; +import static com.greghaskins.spectrum.internal.Declaration.addComposite; import com.greghaskins.spectrum.Block; import com.greghaskins.spectrum.ParameterizedBlock; @@ -13,8 +14,6 @@ import com.greghaskins.spectrum.ParameterizedBlock.SixArgBlock; import com.greghaskins.spectrum.ParameterizedBlock.ThreeArgBlock; import com.greghaskins.spectrum.ParameterizedBlock.TwoArgBlock; -import com.greghaskins.spectrum.internal.DeclarationState; -import com.greghaskins.spectrum.internal.Suite; import java.util.Arrays; @@ -50,9 +49,7 @@ static void feature(final String featureName, final Block block) { * @see #then */ static void scenario(final String scenarioName, final Block block) { - final Suite suite = DeclarationState.instance().getCurrentSuiteBeingDeclared() - .addCompositeSuite("Scenario: " + scenarioName); - DeclarationState.instance().beginDeclaration(suite, block); + addComposite("Scenario: " + scenarioName, block); } /** @@ -155,14 +152,14 @@ static void scenarioOutline(final String name, fi describe("Scenario outline: " + name, () -> { describe("Examples:", () -> { examples.rows().forEach(example -> { - describe(example.toString(), () -> example.runDeclaration(block)); + addComposite(example.toString(), () -> example.runDeclaration(block)); }); }); }); } /** - * Construct an Examples table for {@link scenarioOutline}. Used this method to compose individual + * Construct an Examples table for {@link #scenarioOutline}. Used this method to compose individual * rows created with {@link #example} type methods into a type-implicit container. You should try * to lay out your examples like a data table as that's what they essentially are. Better than * just providing some primitives in an example block would be to provide some objects with fields diff --git a/src/main/java/com/greghaskins/spectrum/dsl/specification/Specification.java b/src/main/java/com/greghaskins/spectrum/dsl/specification/Specification.java index 0ec9fc1..c77b7b6 100644 --- a/src/main/java/com/greghaskins/spectrum/dsl/specification/Specification.java +++ b/src/main/java/com/greghaskins/spectrum/dsl/specification/Specification.java @@ -3,6 +3,7 @@ import static com.greghaskins.spectrum.Configure.focus; import static com.greghaskins.spectrum.Configure.ignore; import static com.greghaskins.spectrum.Configure.with; +import static com.greghaskins.spectrum.internal.Declaration.addSuiteInternal; import static com.greghaskins.spectrum.internal.hooks.AfterHook.after; import static com.greghaskins.spectrum.internal.hooks.BeforeHook.before; @@ -19,6 +20,8 @@ import org.junit.AssumptionViolatedException; +import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Supplier; public interface Specification { @@ -31,11 +34,7 @@ public interface Specification { * define each expected behavior */ static void describe(final String context, final Block block) { - final Suite suite = DeclarationState.instance() - .getCurrentSuiteBeingDeclared() - .addSuite(context); - suite.applyPreconditions(block); - DeclarationState.instance().beginDeclaration(suite, block); + addSuiteInternal(parent -> parent.addSuite(context), block); } /** diff --git a/src/main/java/com/greghaskins/spectrum/internal/Declaration.java b/src/main/java/com/greghaskins/spectrum/internal/Declaration.java new file mode 100644 index 0000000..94fbfe6 --- /dev/null +++ b/src/main/java/com/greghaskins/spectrum/internal/Declaration.java @@ -0,0 +1,41 @@ +package com.greghaskins.spectrum.internal; + +import com.greghaskins.spectrum.Block; + +import java.util.function.Function; + +/** + * Helper functions used by {@link com.greghaskins.spectrum.dsl.specification.Specification} and similar + * DSL-specific classes to build specs. + */ +public interface Declaration { + + /** + * Declare a general purpose composite test - this is a suite that runs as an atomic test in + * itself. This is the internal implementation for + * {@link com.greghaskins.spectrum.dsl.gherkin.Gherkin#scenario(String, Block)}. + * + * @param context Description of the context for this composite + * @param block {@link Block} with one or more calls to + * {@link com.greghaskins.spectrum.dsl.specification.Specification#it(String, Block) it} + * or its equivalent, that define each expected behavior + */ + static void addComposite(final String context, final Block block) { + addSuiteInternal(parent -> parent.addCompositeSuite(context), block); + } + + /** + * Common implementation of adding a suite to the current suite under declaration. + * + * @param suiteCreator specialisation for adding the right sort of suite + * @param block {@link Block} with one or more calls to + * {@link com.greghaskins.spectrum.dsl.specification.Specification#it(String, Block) it} + * or its equivalent, that define each expected behavior + */ + static void addSuiteInternal(final Function suiteCreator, final Block block) { + final Suite suite = suiteCreator.apply(DeclarationState.instance() + .getCurrentSuiteBeingDeclared()); + suite.applyPreconditions(block); + DeclarationState.instance().beginDeclaration(suite, block); + } +} diff --git a/src/main/java/com/greghaskins/spectrum/internal/Suite.java b/src/main/java/com/greghaskins/spectrum/internal/Suite.java index 4b44ced..21e7709 100644 --- a/src/main/java/com/greghaskins/spectrum/internal/Suite.java +++ b/src/main/java/com/greghaskins/spectrum/internal/Suite.java @@ -126,7 +126,8 @@ public void addHook(final HookContext hook) { } private Hooks getHooksFor(final Child child) { - Hooks allHooks = this.parent.getInheritableHooks().plus(this.hooks); + Hooks allHooks = isAtomic() ? new Hooks() : this.parent.getInheritableHooks(); + allHooks = allHooks.plus(this.hooks); return child.isAtomic() ? allHooks.forAtomic() : allHooks.forNonAtomic(); } diff --git a/src/test/java/specs/GherkinExampleSpecs.java b/src/test/java/specs/GherkinExampleSpecs.java index 68072ff..69cba03 100644 --- a/src/test/java/specs/GherkinExampleSpecs.java +++ b/src/test/java/specs/GherkinExampleSpecs.java @@ -1,5 +1,8 @@ package specs; +import static com.greghaskins.spectrum.Configure.focus; +import static com.greghaskins.spectrum.Configure.with; +import static com.greghaskins.spectrum.Spectrum.let; import static com.greghaskins.spectrum.dsl.gherkin.Gherkin.and; import static com.greghaskins.spectrum.dsl.gherkin.Gherkin.feature; import static com.greghaskins.spectrum.dsl.gherkin.Gherkin.given; @@ -10,11 +13,16 @@ import static org.junit.Assert.assertThat; import com.greghaskins.spectrum.Spectrum; +import com.greghaskins.spectrum.SpectrumHelper; import com.greghaskins.spectrum.Variable; +import org.junit.runner.Result; import org.junit.runner.RunWith; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; @RunWith(Spectrum.class) public class GherkinExampleSpecs { @@ -54,6 +62,38 @@ public class GherkinExampleSpecs { }); }); + Supplier> list = let(ArrayList::new); + scenario("behaviour of let in gherkin", () -> { + given("the data is set", () -> { + list.get().add("hello"); + }); + + then("the data is still there", () -> { + assertThat(list.get().get(0), is("hello")); + }); + }); + + scenario("application of block configuration", () -> { + Variable result = new Variable<>(); + when("executing a gherkin test suite with a configured block", () -> { + result.set(SpectrumHelper.run( + () -> { + scenario("some scenario", () -> { + then("should have been ignored", () -> { + }); + }); + scenario("some focused scenario", with(focus(), () -> { + then("should not have been ignored", () -> { + }); + })); + })); + }); + then("the block configuration will have applied", () -> { + assertThat(result.get().getRunCount(), is(1)); + assertThat(result.get().getIgnoreCount(), is(1)); + }); + }); + }); } } diff --git a/src/test/java/specs/ParameterizedExampleSpecs.java b/src/test/java/specs/ParameterizedExampleSpecs.java index d0de609..7cb8998 100644 --- a/src/test/java/specs/ParameterizedExampleSpecs.java +++ b/src/test/java/specs/ParameterizedExampleSpecs.java @@ -1,5 +1,6 @@ package specs; +import static com.greghaskins.spectrum.Spectrum.let; import static com.greghaskins.spectrum.dsl.gherkin.Gherkin.and; import static com.greghaskins.spectrum.dsl.gherkin.Gherkin.example; import static com.greghaskins.spectrum.dsl.gherkin.Gherkin.given; @@ -10,6 +11,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertTrue; import com.greghaskins.spectrum.Spectrum; import com.greghaskins.spectrum.Variable; @@ -18,6 +20,10 @@ import jdk.nashorn.api.scripting.NashornScriptEngine; import org.junit.runner.RunWith; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Supplier; + import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; @@ -118,6 +124,24 @@ public class ParameterizedExampleSpecs { example("bye", -1.5)) ); + + Supplier> set = let(HashSet::new); + scenarioOutline("behaviour of let inside scenario outline", + (first, second) -> { + given("set contains first", () -> { + set.get().add(first); + }); + when("set has second added", () -> { + set.get().add(second); + }); + then("both are found in the set", () -> { + assertTrue(set.get().contains(first)); + assertTrue(set.get().contains(second)); + }); + }, + withExamples( + example("a", "b"), + example("b", "c"))); } // dummy class under test