diff --git a/core/src/main/java/cucumber/api/Scenario.java b/core/src/main/java/cucumber/api/Scenario.java index e00e1ff58f..59569a7fe2 100644 --- a/core/src/main/java/cucumber/api/Scenario.java +++ b/core/src/main/java/cucumber/api/Scenario.java @@ -47,4 +47,9 @@ public interface Scenario { void write(String text); String getName(); + + /** + * @return the id of the Scenario. + */ + String getId(); } diff --git a/core/src/main/java/cucumber/runtime/Runtime.java b/core/src/main/java/cucumber/runtime/Runtime.java index e4bc9d8973..36f970f26a 100644 --- a/core/src/main/java/cucumber/runtime/Runtime.java +++ b/core/src/main/java/cucumber/runtime/Runtime.java @@ -13,6 +13,7 @@ import gherkin.formatter.model.DocString; import gherkin.formatter.model.Match; import gherkin.formatter.model.Result; +import gherkin.formatter.model.Scenario; import gherkin.formatter.model.Step; import gherkin.formatter.model.Tag; @@ -133,14 +134,14 @@ private void writeStepdefsJson() { glue.writeStepdefsJson(resourceLoader, runtimeOptions.getFeaturePaths(), runtimeOptions.getDotCucumber()); } - public void buildBackendWorlds(Reporter reporter, Set tags, String scenarioName) { + public void buildBackendWorlds(Reporter reporter, Set tags, Scenario gherkinScenario) { for (Backend backend : backends) { backend.buildWorld(); } undefinedStepsTracker.reset(); //TODO: this is the initial state of the state machine, it should not go here, but into something else skipNextStep = false; - scenarioResult = new ScenarioImpl(reporter, tags, scenarioName); + scenarioResult = new ScenarioImpl(reporter, tags, gherkinScenario); } public void disposeBackendWorlds() { diff --git a/core/src/main/java/cucumber/runtime/ScenarioImpl.java b/core/src/main/java/cucumber/runtime/ScenarioImpl.java index 85826c6d7c..db08d26e70 100644 --- a/core/src/main/java/cucumber/runtime/ScenarioImpl.java +++ b/core/src/main/java/cucumber/runtime/ScenarioImpl.java @@ -19,11 +19,13 @@ public class ScenarioImpl implements Scenario { private final Reporter reporter; private final Set tags; private final String scenarioName; + private final String scenarioId; - public ScenarioImpl(Reporter reporter, Set tags, String scenarioName) { + public ScenarioImpl(Reporter reporter, Set tags, gherkin.formatter.model.Scenario gherkinScenario) { this.reporter = reporter; this.tags = tags; - this.scenarioName = scenarioName; + this.scenarioName = gherkinScenario.getName(); + this.scenarioId = gherkinScenario.getId(); } void add(Result result) { @@ -68,4 +70,9 @@ public void write(String text) { public String getName() { return scenarioName; } + + @Override + public String getId() { + return scenarioId; + } } diff --git a/core/src/main/java/cucumber/runtime/model/CucumberScenario.java b/core/src/main/java/cucumber/runtime/model/CucumberScenario.java index af87c0115e..643bc6c640 100644 --- a/core/src/main/java/cucumber/runtime/model/CucumberScenario.java +++ b/core/src/main/java/cucumber/runtime/model/CucumberScenario.java @@ -35,7 +35,7 @@ public CucumberBackground getCucumberBackground() { @Override public void run(Formatter formatter, Reporter reporter, Runtime runtime) { Set tags = tagsAndInheritedTags(); - runtime.buildBackendWorlds(reporter, tags, scenario.getName()); + runtime.buildBackendWorlds(reporter, tags, scenario); try { formatter.startOfScenarioLifeCycle((Scenario) getGherkinModel()); } catch (Throwable ignore) { diff --git a/core/src/test/java/cucumber/runtime/HookOrderTest.java b/core/src/test/java/cucumber/runtime/HookOrderTest.java index 105c7ee81e..19f5cacfa5 100644 --- a/core/src/test/java/cucumber/runtime/HookOrderTest.java +++ b/core/src/test/java/cucumber/runtime/HookOrderTest.java @@ -31,7 +31,7 @@ public void buildMockWorld() { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); RuntimeOptions runtimeOptions = new RuntimeOptions(""); runtime = new Runtime(mock(ResourceLoader.class), classLoader, asList(mock(Backend.class)), runtimeOptions); - runtime.buildBackendWorlds(null, Collections.emptySet(), "mock scenario"); + runtime.buildBackendWorlds(null, Collections.emptySet(), mock(gherkin.formatter.model.Scenario.class)); glue = runtime.getGlue(); } diff --git a/core/src/test/java/cucumber/runtime/RuntimeTest.java b/core/src/test/java/cucumber/runtime/RuntimeTest.java index f1f3e18d99..06a75bf536 100644 --- a/core/src/test/java/cucumber/runtime/RuntimeTest.java +++ b/core/src/test/java/cucumber/runtime/RuntimeTest.java @@ -7,6 +7,7 @@ import cucumber.runtime.io.ResourceLoader; import cucumber.runtime.model.CucumberFeature; import gherkin.I18n; +import gherkin.formatter.Formatter; import gherkin.formatter.JSONFormatter; import gherkin.formatter.Reporter; import gherkin.formatter.model.Step; @@ -14,6 +15,7 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.internal.AssumptionViolatedException; +import org.mockito.ArgumentCaptor; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -29,13 +31,14 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyCollectionOf; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class RuntimeTest { @@ -208,9 +211,7 @@ public void should_add_passed_result_to_the_summary_counter() throws Exception { StepDefinitionMatch match = mock(StepDefinitionMatch.class); Runtime runtime = createRuntimeWithMockedGlue(match, "--monochrome"); - runtime.buildBackendWorlds(reporter, Collections.emptySet(), "test scenario"); - runStep(reporter, runtime); - runtime.disposeBackendWorlds(); + runScenario(reporter, runtime, stepCount(1)); runtime.printStats(new PrintStream(baos)); assertThat(baos.toString(), startsWith(String.format( @@ -225,9 +226,7 @@ public void should_add_pending_result_to_the_summary_counter() throws Throwable StepDefinitionMatch match = createExceptionThrowingMatch(new PendingException()); Runtime runtime = createRuntimeWithMockedGlue(match, "--monochrome"); - runtime.buildBackendWorlds(reporter, Collections.emptySet(), "test scenario"); - runStep(reporter, runtime); - runtime.disposeBackendWorlds(); + runScenario(reporter, runtime, stepCount(1)); runtime.printStats(new PrintStream(baos)); assertThat(baos.toString(), startsWith(String.format( @@ -242,9 +241,7 @@ public void should_add_failed_result_to_the_summary_counter() throws Throwable { StepDefinitionMatch match = createExceptionThrowingMatch(new Exception()); Runtime runtime = createRuntimeWithMockedGlue(match, "--monochrome"); - runtime.buildBackendWorlds(reporter, Collections.emptySet(), "test scenario"); - runStep(reporter, runtime); - runtime.disposeBackendWorlds(); + runScenario(reporter, runtime, stepCount(1)); runtime.printStats(new PrintStream(baos)); assertThat(baos.toString(), startsWith(String.format( @@ -258,9 +255,7 @@ public void should_add_ambiguous_match_as_failed_result_to_the_summary_counter() Reporter reporter = mock(Reporter.class); Runtime runtime = createRuntimeWithMockedGlueWithAmbiguousMatch("--monochrome"); - runtime.buildBackendWorlds(reporter, Collections.emptySet(), "test scenario"); - runStep(reporter, runtime); - runtime.disposeBackendWorlds(); + runScenario(reporter, runtime, stepCount(1)); runtime.printStats(new PrintStream(baos)); assertThat(baos.toString(), startsWith(String.format( @@ -275,10 +270,7 @@ public void should_add_skipped_result_to_the_summary_counter() throws Throwable StepDefinitionMatch match = createExceptionThrowingMatch(new Exception()); Runtime runtime = createRuntimeWithMockedGlue(match, "--monochrome"); - runtime.buildBackendWorlds(reporter, Collections.emptySet(), "test scenario"); - runStep(reporter, runtime); - runStep(reporter, runtime); - runtime.disposeBackendWorlds(); + runScenario(reporter, runtime, stepCount(2)); runtime.printStats(new PrintStream(baos)); assertThat(baos.toString(), startsWith(String.format( @@ -292,9 +284,7 @@ public void should_add_undefined_result_to_the_summary_counter() throws Throwabl Reporter reporter = mock(Reporter.class); Runtime runtime = createRuntimeWithMockedGlue(null, "--monochrome"); - runtime.buildBackendWorlds(reporter, Collections.emptySet(), "test scenario"); - runStep(reporter, runtime); - runtime.disposeBackendWorlds(); + runScenario(reporter, runtime, stepCount(1)); runtime.printStats(new PrintStream(baos)); assertThat(baos.toString(), startsWith(String.format( @@ -310,10 +300,7 @@ public void should_fail_the_scenario_if_before_fails() throws Throwable { HookDefinition hook = createExceptionThrowingHook(); Runtime runtime = createRuntimeWithMockedGlue(match, hook, true, "--monochrome"); - runtime.buildBackendWorlds(reporter, Collections.emptySet(), "test scenario"); - runtime.runBeforeHooks(reporter, Collections.emptySet()); - runStep(reporter, runtime); - runtime.disposeBackendWorlds(); + runScenario(reporter, runtime, stepCount(1)); runtime.printStats(new PrintStream(baos)); assertThat(baos.toString(), startsWith(String.format( @@ -329,10 +316,7 @@ public void should_fail_the_scenario_if_after_fails() throws Throwable { HookDefinition hook = createExceptionThrowingHook(); Runtime runtime = createRuntimeWithMockedGlue(match, hook, false, "--monochrome"); - runtime.buildBackendWorlds(reporter, Collections.emptySet(), "test scenario"); - runStep(reporter, runtime); - runtime.runAfterHooks(reporter, Collections.emptySet()); - runtime.disposeBackendWorlds(); + runScenario(reporter, runtime, stepCount(1)); runtime.printStats(new PrintStream(baos)); assertThat(baos.toString(), startsWith(String.format( @@ -340,6 +324,44 @@ public void should_fail_the_scenario_if_after_fails() throws Throwable { "1 Steps (1 passed)%n"))); } + @Test + public void should_make_scenario_name_available_to_hooks() throws Throwable { + CucumberFeature feature = TestHelper.feature("path/test.feature", + "Feature: feature name\n" + + " Scenario: scenario name\n" + + " Given first step\n" + + " When second step\n" + + " Then third step\n"); + HookDefinition beforeHook = mock(HookDefinition.class); + when(beforeHook.matches(anyCollectionOf(Tag.class))).thenReturn(true); + + Runtime runtime = createRuntimeWithMockedGlue(mock(StepDefinitionMatch.class), beforeHook, true); + feature.run(mock(Formatter.class), mock(Reporter.class), runtime); + + ArgumentCaptor capturedScenario = ArgumentCaptor.forClass(Scenario.class); + verify(beforeHook).execute(capturedScenario.capture()); + assertEquals("scenario name", capturedScenario.getValue().getName()); + } + + @Test + public void should_make_scenario_id_available_to_hooks() throws Throwable { + CucumberFeature feature = TestHelper.feature("path/test.feature", + "Feature: feature name\n" + + " Scenario: scenario name\n" + + " Given first step\n" + + " When second step\n" + + " Then third step\n"); + HookDefinition beforeHook = mock(HookDefinition.class); + when(beforeHook.matches(anyCollectionOf(Tag.class))).thenReturn(true); + + Runtime runtime = createRuntimeWithMockedGlue(mock(StepDefinitionMatch.class), beforeHook, true); + feature.run(mock(Formatter.class), mock(Reporter.class), runtime); + + ArgumentCaptor capturedScenario = ArgumentCaptor.forClass(Scenario.class); + verify(beforeHook).execute(capturedScenario.capture()); + assertEquals("feature-name;scenario-name", capturedScenario.getValue().getId()); + } + private StepDefinitionMatch createExceptionThrowingMatch(Exception exception) throws Throwable { StepDefinitionMatch match = mock(StepDefinitionMatch.class); doThrow(exception).when(match).runStep((I18n)any()); @@ -392,7 +414,7 @@ private Runtime createRuntime(ResourceLoader resourceLoader, ClassLoader classLo } private Runtime createRuntimeWithMockedGlue(StepDefinitionMatch match, String... runtimeArgs) { - return createRuntimeWithMockedGlue(match, false, null, false, runtimeArgs); + return createRuntimeWithMockedGlue(match, false, mock(HookDefinition.class), false, runtimeArgs); } private Runtime createRuntimeWithMockedGlue(StepDefinitionMatch match, HookDefinition hook, boolean isBefore, @@ -401,7 +423,7 @@ private Runtime createRuntimeWithMockedGlue(StepDefinitionMatch match, HookDefin } private Runtime createRuntimeWithMockedGlueWithAmbiguousMatch(String... runtimeArgs) { - return createRuntimeWithMockedGlue(mock(StepDefinitionMatch.class), true, null, false, runtimeArgs); + return createRuntimeWithMockedGlue(mock(StepDefinitionMatch.class), true, mock(HookDefinition.class), false, runtimeArgs); } private Runtime createRuntimeWithMockedGlue(StepDefinitionMatch match, boolean isAmbiguous, HookDefinition hook, @@ -434,4 +456,19 @@ private void mockHook(RuntimeGlue glue, HookDefinition hook, boolean isBefore) { when(glue.getAfterHooks()).thenReturn(Arrays.asList(hook)); } } + + private void runScenario(Reporter reporter, Runtime runtime, int stepCount) { + gherkin.formatter.model.Scenario gherkinScenario = mock(gherkin.formatter.model.Scenario.class); + runtime.buildBackendWorlds(reporter, Collections.emptySet(), gherkinScenario); + runtime.runBeforeHooks(reporter, Collections.emptySet()); + for (int i = 0; i < stepCount; ++i) { + runStep(reporter, runtime); + } + runtime.runAfterHooks(reporter, Collections.emptySet()); + runtime.disposeBackendWorlds(); + } + + private int stepCount(int stepCount) { + return stepCount; + } } diff --git a/core/src/test/java/cucumber/runtime/ScenarioResultTest.java b/core/src/test/java/cucumber/runtime/ScenarioResultTest.java index 38d118e2fb..e7fc636b6c 100644 --- a/core/src/test/java/cucumber/runtime/ScenarioResultTest.java +++ b/core/src/test/java/cucumber/runtime/ScenarioResultTest.java @@ -2,6 +2,7 @@ import gherkin.formatter.Reporter; import gherkin.formatter.model.Result; +import gherkin.formatter.model.Scenario; import gherkin.formatter.model.Tag; import org.junit.Test; @@ -14,7 +15,7 @@ public class ScenarioResultTest { private Reporter reporter = mock(Reporter.class); - private ScenarioImpl s = new ScenarioImpl(reporter, Collections.emptySet(), "test scenario"); + private ScenarioImpl s = new ScenarioImpl(reporter, Collections.emptySet(), mock(Scenario.class)); @Test public void no_steps_is_passed() throws Exception { diff --git a/java/src/test/java/cucumber/runtime/java/JavaStepDefinitionTest.java b/java/src/test/java/cucumber/runtime/java/JavaStepDefinitionTest.java index e9ddc02217..cfe11f70eb 100644 --- a/java/src/test/java/cucumber/runtime/java/JavaStepDefinitionTest.java +++ b/java/src/test/java/cucumber/runtime/java/JavaStepDefinitionTest.java @@ -12,6 +12,7 @@ import gherkin.formatter.model.Comment; import gherkin.formatter.model.Match; import gherkin.formatter.model.Result; +import gherkin.formatter.model.Scenario; import gherkin.formatter.model.Step; import gherkin.formatter.model.Tag; import org.junit.Test; @@ -69,7 +70,7 @@ public void throws_ambiguous_when_two_matches_are_found() throws Throwable { backend.addStepDefinition(THREE_BLIND_ANIMALS.getAnnotation(Given.class), THREE_BLIND_ANIMALS); Reporter reporter = mock(Reporter.class); - runtime.buildBackendWorlds(reporter, Collections.emptySet(), "test scenario"); + runtime.buildBackendWorlds(reporter, Collections.emptySet(), mock(Scenario.class)); Tag tag = new Tag("@foo", 0); runtime.runBeforeHooks(reporter, asSet(tag)); runtime.runStep("some.feature", new Step(NO_COMMENTS, "Given ", "three blind mice", 1, null, null), reporter, ENGLISH); @@ -113,7 +114,7 @@ public void embedding(String mimeType, byte[] data) { public void write(String text) { } }; - runtime.buildBackendWorlds(reporter, Collections.emptySet(), "test scenario"); + runtime.buildBackendWorlds(reporter, Collections.emptySet(), mock(Scenario.class)); Tag tag = new Tag("@foo", 0); Set tags = asSet(tag); runtime.runBeforeHooks(reporter, tags); diff --git a/scala/src/test/scala/cucumber/api/scala/ScalaDslTest.scala b/scala/src/test/scala/cucumber/api/scala/ScalaDslTest.scala index ad153876c6..d2a136a4e0 100644 --- a/scala/src/test/scala/cucumber/api/scala/ScalaDslTest.scala +++ b/scala/src/test/scala/cucumber/api/scala/ScalaDslTest.scala @@ -21,6 +21,8 @@ class ScalaDslTest { def write(p1: String) {} def getName = "" + + def getId = "" } @Test @@ -129,7 +131,7 @@ class ScalaDslTest { assertEquals(1, Dummy.stepDefinitions.size) val step = Dummy.stepDefinitions.head - assertEquals("ScalaDslTest.scala:125", step.getLocation(true)) // be careful with formatting or this test will break + assertEquals("ScalaDslTest.scala:127", step.getLocation(true)) // be careful with formatting or this test will break assertEquals("x", step.getPattern) step.execute(new I18n("en"), Array()) assertTrue(called)