From 2f9a4db5ab43214cb833f3f888c1907ce071605b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Rasmusson?= Date: Sun, 23 Feb 2014 18:08:51 +0100 Subject: [PATCH 1/2] Expose Scenario id to step definitions Extend the Scenario interface with the method getId(). Then it is avialable to step definition through the Scenario argument to hooks. The scenario id is esentially ; or ;;;. --- core/src/main/java/cucumber/api/Scenario.java | 8 ++ .../main/java/cucumber/runtime/Runtime.java | 5 +- .../java/cucumber/runtime/ScenarioImpl.java | 11 +- .../runtime/model/CucumberScenario.java | 2 +- .../java/cucumber/runtime/HookOrderTest.java | 2 +- .../java/cucumber/runtime/RuntimeTest.java | 101 ++++++++++++------ .../cucumber/runtime/ScenarioResultTest.java | 3 +- .../runtime/java/JavaStepDefinitionTest.java | 5 +- .../cucumber/api/scala/ScalaDslTest.scala | 4 +- 9 files changed, 99 insertions(+), 42 deletions(-) diff --git a/core/src/main/java/cucumber/api/Scenario.java b/core/src/main/java/cucumber/api/Scenario.java index e00e1ff58f..eaf53063a5 100644 --- a/core/src/main/java/cucumber/api/Scenario.java +++ b/core/src/main/java/cucumber/api/Scenario.java @@ -46,5 +46,13 @@ public interface Scenario { */ void write(String text); + /** + * @return the name of the Scenario. + */ 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) From df74cdbb3b8e0dc6e5dd3c6489da4e4f37f712e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Rasmusson?= Date: Thu, 26 Jun 2014 11:24:38 +0200 Subject: [PATCH 2/2] Javadoc for getName already added to master --- core/src/main/java/cucumber/api/Scenario.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/src/main/java/cucumber/api/Scenario.java b/core/src/main/java/cucumber/api/Scenario.java index eaf53063a5..59569a7fe2 100644 --- a/core/src/main/java/cucumber/api/Scenario.java +++ b/core/src/main/java/cucumber/api/Scenario.java @@ -46,9 +46,6 @@ public interface Scenario { */ void write(String text); - /** - * @return the name of the Scenario. - */ String getName(); /**