Skip to content

Commit

Permalink
JBEHAVE-1586 Add ability to use scenario ExamplesTable parameters in …
Browse files Browse the repository at this point in the history
…Lifecycle steps
  • Loading branch information
valfirst authored Sep 7, 2023
1 parent 9cd2879 commit fa486fa
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ private PerformableStory performableStory(RunContext context, Story story, Map<S
if (!storyExcluded) {

Map<Stage, PerformableSteps> lifecycleSteps = context.lifecycleSteps(story.getLifecycle(), storyMeta,
Scope.STORY);
Scope.STORY, new HashMap<>()); // TODO: Rework to use ExamplesTable row here

performableStory.addBeforeSteps(ExecutionType.SYSTEM, context.beforeStorySteps(storyMeta));
performableStory.addBeforeSteps(ExecutionType.USER, lifecycleSteps.get(Stage.BEFORE));
Expand Down Expand Up @@ -266,7 +266,7 @@ private Meta parameterMeta(RunContext context, Map<String, String> parameters) {
private void addStepsWithLifecycle(AbstractPerformableScenario performableScenario, RunContext context,
Lifecycle lifecycle, Map<String, String> parameters, Scenario scenario, Meta storyAndScenarioMeta) {
Map<Stage, PerformableSteps> lifecycleSteps = context.lifecycleSteps(lifecycle, storyAndScenarioMeta,
Scope.SCENARIO);
Scope.SCENARIO, parameters);

performableScenario.addBeforeSteps(ExecutionType.SYSTEM,
context.beforeScenarioSteps(storyAndScenarioMeta, ScenarioType.ANY));
Expand Down Expand Up @@ -679,10 +679,11 @@ public PerformableSteps afterScenarioSteps(Meta storyAndScenarioMeta, ScenarioTy
.collectAfterScenarioSteps(allStepCandidates.getAfterScenarioSteps(type), storyAndScenarioMeta));
}

private Map<Stage, PerformableSteps> lifecycleSteps(Lifecycle lifecycle, Meta meta, Scope scope) {
private Map<Stage, PerformableSteps> lifecycleSteps(Lifecycle lifecycle, Meta meta, Scope scope,
Map<String, String> parameters) {
MatchingStepMonitor monitor = new MatchingStepMonitor(configuration.stepMonitor());
Map<Stage, List<Step>> steps = configuration.stepCollector().collectLifecycleSteps(
allStepCandidates.getRegularSteps(), lifecycle, meta, scope, monitor);
allStepCandidates.getRegularSteps(), lifecycle, meta, scope, parameters, monitor);
Map<Stage, PerformableSteps> performableSteps = new EnumMap<>(Stage.class);
for (Map.Entry<Stage, List<Step>> entry : steps.entrySet()) {
performableSteps.put(entry.getKey(), new PerformableSteps(entry.getValue(), monitor.matched()));
Expand All @@ -695,7 +696,7 @@ private PerformableSteps scenarioSteps(Lifecycle lifecycle, Meta meta, Scenario
MatchingStepMonitor monitor = new MatchingStepMonitor(configuration.stepMonitor());
StepCollector stepCollector = configuration.stepCollector();
Map<Stage, List<Step>> beforeOrAfterStepSteps = stepCollector.collectLifecycleSteps(
allStepCandidates.getRegularSteps(), lifecycle, meta, Scope.STEP, monitor);
allStepCandidates.getRegularSteps(), lifecycle, meta, Scope.STEP, parameters, monitor);
List<Step> steps = new LinkedList<>();
for (Step step : stepCollector.collectScenarioSteps(allStepCandidates.getRegularSteps(), scenario,
parameters, monitor)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -64,13 +63,12 @@ public List<Step> collectAfterScenarioSteps(List<BeforeOrAfterStep> afterScenari

@Override
public Map<Stage, List<Step>> collectLifecycleSteps(List<StepCandidate> stepCandidates, Lifecycle lifecycle,
Meta storyAndScenarioMeta, Scope scope, StepMonitor stepMonitor) {
Map<String, String> namedParameters = new HashMap<>();
List<Step> beforeSteps = collectMatchedSteps(lifecycle.getBeforeSteps(scope), namedParameters, stepCandidates,
Meta storyAndScenarioMeta, Scope scope, Map<String, String> parameters, StepMonitor stepMonitor) {
List<Step> beforeSteps = collectMatchedSteps(lifecycle.getBeforeSteps(scope), parameters, stepCandidates,
null, stepMonitor);
List<Step> afterSteps = Stream.of(Outcome.values())
.map(outcome -> collectMatchedSteps(lifecycle.getAfterSteps(scope, outcome, storyAndScenarioMeta),
namedParameters, stepCandidates, outcome, stepMonitor))
parameters, stepCandidates, outcome, stepMonitor))
.flatMap(List::stream)
.collect(Collectors.toList());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,12 @@ enum Stage {
* @param lifecycle the {@link Lifecycle}
* @param storyAndScenarioMeta the story and scenario {@link Meta} parameters
* @param scope the {@link Scope} of the lifecycle steps
* @param parameters the parameters
* @param stepMonitor the {@link StepMonitor}
* @return A List of executable {@link Step}s
*/
Map<Stage, List<Step>> collectLifecycleSteps(List<StepCandidate> stepCandidates, Lifecycle lifecycle,
Meta storyAndScenarioMeta, Scope scope, StepMonitor stepMonitor);
Meta storyAndScenarioMeta, Scope scope, Map<String, String> parameters, StepMonitor stepMonitor);

/**
* Collects all of the {@link Step}s to execute for a scenario.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import static org.mockito.Mockito.when;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -72,6 +73,7 @@
import org.jbehave.core.steps.Timing;
import org.jbehave.core.steps.context.StepsContext;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;

class PerformableTreeBehaviour {
Expand Down Expand Up @@ -112,7 +114,7 @@ void shouldAddExcludedPerformableScenariosToPerformableStory() {
lifecycleSteps.put(Stage.BEFORE, emptyList());
lifecycleSteps.put(Stage.AFTER, emptyList());
when(stepCollector.collectLifecycleSteps(eq(stepCandidates), eq(story.getLifecycle()), eq(storyMeta),
eq(Scope.STORY), any(MatchingStepMonitor.class))).thenReturn(lifecycleSteps);
eq(Scope.STORY), eq(new HashMap<>()), any(MatchingStepMonitor.class))).thenReturn(lifecycleSteps);

PerformableTree performableTree = new PerformableTree();
PerformableTree.RunContext runContext = performableTree.newRunContext(configuration, allStepCandidates,
Expand All @@ -124,7 +126,7 @@ void shouldAddExcludedPerformableScenariosToPerformableStory() {
InOrder ordered = inOrder(stepCollector);
ordered.verify(stepCollector).collectBeforeOrAfterStoriesSteps(beforeStories);
ordered.verify(stepCollector).collectLifecycleSteps(eq(stepCandidates), eq(story.getLifecycle()),
eq(storyMeta), eq(Scope.STORY), any(MatchingStepMonitor.class));
eq(storyMeta), eq(Scope.STORY), eq(new HashMap<>()), any(MatchingStepMonitor.class));
ordered.verify(stepCollector).collectBeforeOrAfterStorySteps(beforeStory, storyMeta);
ordered.verify(stepCollector).collectBeforeOrAfterStorySteps(afterStory, storyMeta);
ordered.verify(stepCollector).collectBeforeOrAfterStoriesSteps(afterStories);
Expand Down Expand Up @@ -172,6 +174,7 @@ void shouldResetCurrentStoryControls() {
assertThat(storyControls.skipScenariosAfterFailure(), is(false));
}

@SuppressWarnings("unchecked")
@Test
void shouldReplaceParameters() {
ParameterControls parameterControls = new ParameterControls();
Expand Down Expand Up @@ -216,14 +219,17 @@ void shouldReplaceParameters() {
Map<Stage, List<Step>> lifecycleSteps = new EnumMap<>(Stage.class);
lifecycleSteps.put(Stage.BEFORE, emptyList());
lifecycleSteps.put(Stage.AFTER, emptyList());

ArgumentCaptor<Map<String, String>> storyParametersCaptor = ArgumentCaptor.forClass(Map.class);
when(stepCollector.collectLifecycleSteps(eq(emptyList()), eq(lifecycle), isEmptyMeta(), eq(Scope.STORY),
any(MatchingStepMonitor.class))).thenReturn(lifecycleSteps);
storyParametersCaptor.capture(), any(MatchingStepMonitor.class))).thenReturn(lifecycleSteps);

ArgumentCaptor<Map<String, String>> scenarioParametersCaptor = ArgumentCaptor.forClass(Map.class);
when(stepCollector.collectLifecycleSteps(eq(emptyList()), eq(lifecycle), isEmptyMeta(), eq(Scope.SCENARIO),
any(MatchingStepMonitor.class))).thenReturn(lifecycleSteps);
scenarioParametersCaptor.capture(), any(MatchingStepMonitor.class))).thenReturn(lifecycleSteps);

PerformableTree performableTree = new PerformableTree();
RunContext context = createRunContext(configuration, performableTree, mock(BatchFailures.class),
singletonList(story));
createRunContext(configuration, performableTree, mock(BatchFailures.class), singletonList(story));
List<PerformableTree.PerformableScenario> performableScenarios = performableTree.getRoot().getStories().get(0)
.getScenarios();

Expand All @@ -250,6 +256,17 @@ void shouldReplaceParameters() {
assertThat(examplePerformableScenarios.get(1).getParameters().get("var1"), is("Gd"));
assertThat(examplePerformableScenarios.get(1).getParameters().get("var2"), is("d"));
assertThat(examplePerformableScenarios.get(1).getParameters().get("var3"), is("d"));

List<Map<String, String>> storyParameters = new ArrayList<>();
storyParameters.add(new HashMap<>());
assertEquals(storyParameters, storyParametersCaptor.getAllValues());

List<Map<String, String>> scenarioParameters = new ArrayList<>();
scenarioParameters.add(performableScenarios.get(0).getExamples().get(0).getParameters());
scenarioParameters.add(performableScenarios.get(0).getExamples().get(1).getParameters());
scenarioParameters.add(performableScenarios.get(1).getExamples().get(0).getParameters());
scenarioParameters.add(performableScenarios.get(1).getExamples().get(1).getParameters());
assertEquals(scenarioParameters, scenarioParametersCaptor.getAllValues());
}

private Map<String, String> createExamplesRow(String key1, String value1, String key2, String value2) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static java.util.Collections.singletonList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
Expand All @@ -13,6 +14,7 @@
import static org.mockito.Mockito.when;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
Expand All @@ -36,9 +38,11 @@
import org.jbehave.core.steps.StepCollector.Stage;
import org.jbehave.core.steps.StepMonitor;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;

class PerformableTreeConversionBehaviour {

@SuppressWarnings("unchecked")
@Test
void shouldConvertParameters() {
SharpParameterConverters sharpParameterConverters = new SharpParameterConverters();
Expand Down Expand Up @@ -71,16 +75,19 @@ void shouldConvertParameters() {
ExamplesTable storyExamplesTable = ExamplesTable.empty().withRows(
asList(storyExampleFirstRow, storyExampleSecondRow));

Lifecycle lifecycle = mock(Lifecycle.class);
when(lifecycle.getExamplesTable()).thenReturn(storyExamplesTable);
Lifecycle lifecycle = new Lifecycle(storyExamplesTable, new ArrayList<>(), new ArrayList<>());

Map<Stage, List<Step>> lifecycleSteps = new EnumMap<>(Stage.class);
lifecycleSteps.put(Stage.BEFORE, emptyList());
lifecycleSteps.put(Stage.AFTER, emptyList());

ArgumentCaptor<Map<String, String>> storyParametersCaptor = ArgumentCaptor.forClass(Map.class);
when(stepCollector.collectLifecycleSteps(eq(emptyList()), eq(lifecycle), isEmptyMeta(), eq(Scope.STORY),
any(MatchingStepMonitor.class))).thenReturn(lifecycleSteps);
storyParametersCaptor.capture(), any(MatchingStepMonitor.class))).thenReturn(lifecycleSteps);

ArgumentCaptor<Map<String, String>> scenarioParametersCaptor = ArgumentCaptor.forClass(Map.class);
when(stepCollector.collectLifecycleSteps(eq(emptyList()), eq(lifecycle), isEmptyMeta(), eq(Scope.SCENARIO),
any(MatchingStepMonitor.class))).thenReturn(lifecycleSteps);
scenarioParametersCaptor.capture(), any(MatchingStepMonitor.class))).thenReturn(lifecycleSteps);

Map<String,String> scenarioExample = new HashMap<>();
scenarioExample.put("var1","#E");
Expand Down Expand Up @@ -129,6 +136,17 @@ void shouldConvertParameters() {
assertThatAreEqual("gddGdD", examplePerformableScenarios.get(1).getParameters().get("var1"));
assertThatAreEqual("dD", examplePerformableScenarios.get(1).getParameters().get("var2"));
assertThatAreEqual("hH", examplePerformableScenarios.get(1).getParameters().get("var3"));

List<Map<String, String>> storyParameters = new ArrayList<>();
storyParameters.add(new HashMap<>());
assertEquals(storyParameters, storyParametersCaptor.getAllValues());

List<Map<String, String>> scenarioParameters = new ArrayList<>();
scenarioParameters.add(performableScenarios.get(0).getExamples().get(0).getParameters());
scenarioParameters.add(performableScenarios.get(0).getExamples().get(1).getParameters());
scenarioParameters.add(performableScenarios.get(1).getExamples().get(0).getParameters());
scenarioParameters.add(performableScenarios.get(1).getExamples().get(1).getParameters());
assertEquals(scenarioParameters, scenarioParametersCaptor.getAllValues());
}

private PerformableTree.RunContext spyRunContext(PerformableTree performableTree, Configuration configuration) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,19 @@

@SuppressWarnings("checkstyle:MemberName")
public class ExampleSteps {
private int x;
private long x;
private Map<String, Integer> variables;
private int result;

@Given("a variable x with value $value")
@Alias("ist die Variable x mit dem Wert $value")
public void givenXValue(@Named("value") int value) {
public void givenXValue(@Named("value") long value) {
x = value;
}

@When("I multiply x by $value")
@Alias("ich x mit $value multipliziere")
public void whenImultiplyXBy(@Named("value") int value) {
public void whenImultiplyXBy(@Named("value") long value) {
x = x * value;
}

Expand All @@ -44,7 +44,7 @@ public void whenImultiplyXByOneOf(ExamplesTable param) {

@Then("x should equal $value")
@Alias("ist x gleich $value")
public void thenXshouldBe(@Named("value") int value) {
public void thenXshouldBe(@Named("value") long value) {
assertEquals(value, x);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.jbehave.core.junit.story;

import org.jbehave.core.configuration.MostUsefulConfiguration;
import org.jbehave.core.junit.JUnit4StoryRunner;
import org.jbehave.core.junit.JUnitStory;
import org.jbehave.core.junit.steps.ExampleSteps;
import org.jbehave.core.steps.InjectableStepsFactory;
import org.jbehave.core.steps.InstanceStepsFactory;
import org.junit.runner.RunWith;

@RunWith(JUnit4StoryRunner.class)
public class LifecycleStepsStoryWithExamples extends JUnitStory {

public LifecycleStepsStoryWithExamples() {
useConfiguration(new MostUsefulConfiguration());
JUnit4StoryRunner.recommendedControls(configuredEmbedder());
}

@Override
public InjectableStepsFactory stepsFactory() {
return new InstanceStepsFactory(configuration(), new ExampleSteps());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ void shouldCreateExecutableStepsUponOutcome() {
// When
List<Step> executableSteps = stepCollector.collectLifecycleSteps(
asList(anyCandidate, successCandidate, failureCandidate), lifecycle, Meta.EMPTY, Scope.SCENARIO,
new MatchingStepMonitor()).get(Stage.AFTER);
new HashMap<>(), new MatchingStepMonitor()).get(Stage.AFTER);

// Then
assertThat(executableSteps.size(), equalTo(3));
Expand Down Expand Up @@ -169,7 +169,7 @@ void shouldCreateExecutableStepsUponOutcomeAndScope() {
// When
List<Step> executableSteps = stepCollector.collectLifecycleSteps(
asList(anyCandidate, successCandidate, failureCandidate), lifecycle, Meta.EMPTY, scope,
new MatchingStepMonitor()).get(Stage.AFTER);
new HashMap<>(), new MatchingStepMonitor()).get(Stage.AFTER);

// Then
assertThat(executableSteps.size(), equalTo(3));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
Lifecycle:
Before:
Scope: STORY
Given a variable x with value 1
Scope: SCENARIO
When I multiply x by 2
When I multiply x by <scenarioMultiplier>
Scope: STEP
When I multiply x by 3
When I multiply x by <scenarioMultiplier>
After:
Scope: STEP
When I multiply x by 6
When I multiply x by <scenarioMultiplier>
Scope: SCENARIO
Outcome: ANY
When I multiply x by 7
When I multiply x by <scenarioMultiplier>
Outcome: SUCCESS
When I multiply x by 8
When I multiply x by <scenarioMultiplier>
Scope: STORY
Outcome: ANY
When I multiply x by 9
Outcome: SUCCESS
!-- 1 *
!-- (2 * 4) *
!-- (3 * 4) * 4 * (6 * 4) *
!-- (7 * 4) * (8 * 4) *
!-- (2 * 5) *
!-- (3 * 5) * 5 * (6 * 5) *
!-- (7 * 5) * (8 * 5) *
!-- 9
Then x should equal 2006581248000000

Scenario: x multiplied by 5
When I multiply x by <scenarioMultiplier>
Examples:
| scenarioMultiplier |
| 4 |
| 5 |

0 comments on commit fa486fa

Please sign in to comment.