Skip to content

Commit

Permalink
[Core] Set scenario result as step finishes (#1430)
Browse files Browse the repository at this point in the history
* Clean up pom

* Update scenario result when step finishes

The results of a step were set on the scenario after a test step
finished. This meant that the result of the step was not available in
its after hooks.

By adding the result of the step to the scenario in TestStep we ensure
that scenario always contains the most recent result. This removes the
need to pass the scenario results around.

Additionally:

 * Scenario has moved from cucumber.runtime.ScenarioImpl to
 cucumber.runner.Scenario and made package private. This prevents the
 implementation being used outside of the API. Users of
 ScenarioImpl.getError should use an event listener to create their
 reports.

 * TestStep.run no longer requires a language parameter. With Gherkin v5
 the language is not used anywhere.
  • Loading branch information
mpkorstanje committed Aug 25, 2018
1 parent 532be3e commit 1b0680b
Show file tree
Hide file tree
Showing 36 changed files with 393 additions and 418 deletions.
20 changes: 17 additions & 3 deletions core/src/main/java/cucumber/api/Result.java
Expand Up @@ -3,8 +3,9 @@
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.Comparator; import java.util.Comparator;
import java.util.Objects;


public class Result { public final class Result {


public final static Comparator<Result> SEVERITY = new Comparator<Result>() { public final static Comparator<Result> SEVERITY = new Comparator<Result>() {


Expand All @@ -14,8 +15,6 @@ public int compare(Result a, Result b) {
} }
}; };


private static final long serialVersionUID = 1L;

private final Type status; private final Type status;
private final Long duration; private final Long duration;
private final Throwable error; private final Throwable error;
Expand Down Expand Up @@ -104,4 +103,19 @@ public String toString() {
", error=" + error + ", error=" + error +
'}'; '}';
} }

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Result result = (Result) o;
return status == result.status &&
Objects.equals(duration, result.duration) &&
Objects.equals(error, result.error);
}

@Override
public int hashCode() {
return Objects.hash(status, duration, error);
}
} }
19 changes: 5 additions & 14 deletions core/src/main/java/cucumber/runner/PickleStepTestStep.java
@@ -1,19 +1,13 @@
package cucumber.runner; package cucumber.runner;


import cucumber.api.Result;
import cucumber.api.Scenario;
import cucumber.api.TestCase; import cucumber.api.TestCase;
import cucumber.runtime.DefinitionArgument; import cucumber.runtime.DefinitionArgument;
import cucumber.runtime.PickleStepDefinitionMatch; import cucumber.runtime.PickleStepDefinitionMatch;
import gherkin.pickles.PickleStep; import gherkin.pickles.PickleStep;


import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;


import static cucumber.api.Result.SEVERITY;
import static java.util.Collections.max;

class PickleStepTestStep extends TestStep implements cucumber.api.PickleStepTestStep { class PickleStepTestStep extends TestStep implements cucumber.api.PickleStepTestStep {
private final String uri; private final String uri;
private final PickleStep step; private final PickleStep step;
Expand All @@ -40,23 +34,20 @@ class PickleStepTestStep extends TestStep implements cucumber.api.PickleStepTest
} }


@Override @Override
Result run(TestCase testCase, EventBus bus, String language, Scenario scenario, boolean skipSteps) { boolean run(TestCase testCase, EventBus bus, Scenario scenario, boolean skipSteps) {
boolean skipNextStep = skipSteps; boolean skipNextStep = skipSteps;
List<Result> results = new ArrayList<Result>();


for (HookTestStep before : beforeStepHookSteps) { for (HookTestStep before : beforeStepHookSteps) {
Result result = before.run(testCase, bus, language, scenario, skipSteps); skipNextStep |= before.run(testCase, bus, scenario, skipSteps);
skipNextStep |= !result.is(Result.Type.PASSED);
results.add(result);
} }


results.add(super.run(testCase, bus, language, scenario, skipNextStep)); skipNextStep |= super.run(testCase, bus, scenario, skipNextStep);


for (HookTestStep after : afterStepHookSteps) { for (HookTestStep after : afterStepHookSteps) {
results.add(after.run(testCase, bus, language, scenario, skipSteps)); skipNextStep |= after.run(testCase, bus, scenario, skipSteps);
} }


return max(results, SEVERITY); return skipNextStep;
} }


List<HookTestStep> getBeforeStepHookSteps() { List<HookTestStep> getBeforeStepHookSteps() {
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/cucumber/runner/Runner.java
Expand Up @@ -10,8 +10,8 @@
import cucumber.runtime.Glue; import cucumber.runtime.Glue;
import cucumber.runtime.HookDefinition; import cucumber.runtime.HookDefinition;
import cucumber.runtime.HookDefinitionMatch; import cucumber.runtime.HookDefinitionMatch;
import cucumber.runtime.RuntimeOptions;
import cucumber.runtime.PickleStepDefinitionMatch; import cucumber.runtime.PickleStepDefinitionMatch;
import cucumber.runtime.RuntimeOptions;
import cucumber.runtime.UndefinedPickleStepDefinitionMatch; import cucumber.runtime.UndefinedPickleStepDefinitionMatch;
import gherkin.events.PickleEvent; import gherkin.events.PickleEvent;
import gherkin.pickles.PickleStep; import gherkin.pickles.PickleStep;
Expand Down
@@ -1,50 +1,27 @@
package cucumber.runtime; package cucumber.runner;


import cucumber.api.Result; import cucumber.api.Result;
import cucumber.api.Scenario;
import cucumber.api.TestCase;
import cucumber.api.event.EmbedEvent; import cucumber.api.event.EmbedEvent;
import cucumber.api.event.WriteEvent; import cucumber.api.event.WriteEvent;
import cucumber.runner.EventBus;
import gherkin.events.PickleEvent;
import gherkin.pickles.Pickle;
import gherkin.pickles.PickleLocation;
import gherkin.pickles.PickleTag; import gherkin.pickles.PickleTag;


import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;


import static java.util.Collections.max; import static java.util.Collections.max;


public class ScenarioImpl implements Scenario { class Scenario implements cucumber.api.Scenario {


private final List<Result> stepResults = new ArrayList<Result>(); private final List<Result> stepResults = new ArrayList<Result>();
private final List<PickleTag> tags;
private final String uri;
private final String scenarioName;
private final String scenarioId;
private final List<Integer> scenarioLines;
private final EventBus bus; private final EventBus bus;
private final TestCase testCase; private final TestCase testCase;


public ScenarioImpl(EventBus bus, TestCase testCase, PickleEvent pickleEvent) { Scenario(EventBus bus, TestCase testCase) {
this.bus = bus; this.bus = bus;
this.testCase = testCase; this.testCase = testCase;
Pickle pickle = pickleEvent.pickle;
this.tags = pickle.getTags();
this.uri = pickleEvent.uri;
this.scenarioName = pickle.getName();
List<PickleLocation> locations = pickle.getLocations();
this.scenarioId = pickleEvent.uri + ":" + Integer.toString(locations.get(0).getLine());
ArrayList<Integer> lines = new ArrayList<Integer>();
for (PickleLocation location : locations) {
lines.add(location.getLine());
}
this.scenarioLines = Collections.unmodifiableList(lines);
} }


public void add(Result result) { public void add(Result result) {
Expand All @@ -53,12 +30,12 @@ public void add(Result result) {


@Override @Override
public Collection<String> getSourceTagNames() { public Collection<String> getSourceTagNames() {
Set<String> result = new HashSet<String>(); Set<String> result = new HashSet<>();
for (PickleTag tag : tags) { for (PickleTag tag : testCase.getTags()) {
result.add(tag.getName()); result.add(tag.getName());
} }
// Has to be a List in order for JRuby to convert to Ruby Array. // Has to be a List in order for JRuby to convert to Ruby Array.
return new ArrayList<String>(result); return new ArrayList<>(result);
} }


@Override @Override
Expand Down Expand Up @@ -91,22 +68,22 @@ public void write(String text) {


@Override @Override
public String getName() { public String getName() {
return scenarioName; return testCase.getName();
} }


@Override @Override
public String getId() { public String getId() {
return scenarioId; return testCase.getUri() + ":" + testCase.getLine();
} }


@Override @Override
public String getUri() { public String getUri() {
return uri; return testCase.getUri();
} }


@Override @Override
public List<Integer> getLines() { public List<Integer> getLines() {
return scenarioLines; return testCase.getLines();
} }


public Throwable getError() { public Throwable getError() {
Expand Down
27 changes: 14 additions & 13 deletions core/src/main/java/cucumber/runner/TestCase.java
Expand Up @@ -4,7 +4,6 @@
import cucumber.api.TestStep; import cucumber.api.TestStep;
import cucumber.api.event.TestCaseFinished; import cucumber.api.event.TestCaseFinished;
import cucumber.api.event.TestCaseStarted; import cucumber.api.event.TestCaseStarted;
import cucumber.runtime.ScenarioImpl;
import gherkin.events.PickleEvent; import gherkin.events.PickleEvent;
import gherkin.pickles.PickleLocation; import gherkin.pickles.PickleLocation;
import gherkin.pickles.PickleTag; import gherkin.pickles.PickleTag;
Expand Down Expand Up @@ -35,33 +34,27 @@ void run(EventBus bus) {
boolean skipNextStep = this.dryRun; boolean skipNextStep = this.dryRun;
Long startTime = bus.getTime(); Long startTime = bus.getTime();
bus.send(new TestCaseStarted(startTime, this)); bus.send(new TestCaseStarted(startTime, this));
ScenarioImpl scenarioResult = new ScenarioImpl(bus, this, pickleEvent); Scenario scenario = new Scenario(bus, this);


for (HookTestStep before : beforeHooks) { for (HookTestStep before : beforeHooks) {
Result stepResult = before.run(this, bus, pickleEvent.pickle.getLanguage(), scenarioResult, dryRun); skipNextStep |= before.run(this, bus, scenario, dryRun);
skipNextStep |= !stepResult.is(Result.Type.PASSED);
scenarioResult.add(stepResult);
} }


for (PickleStepTestStep step : testSteps) { for (PickleStepTestStep step : testSteps) {
Result stepResult = step.run(this, bus, pickleEvent.pickle.getLanguage(), scenarioResult, skipNextStep); skipNextStep |= step.run(this, bus, scenario, skipNextStep);
skipNextStep |= !stepResult.is(Result.Type.PASSED);
scenarioResult.add(stepResult);
} }


for (HookTestStep after : afterHooks) { for (HookTestStep after : afterHooks) {
Result stepResult = after.run(this, bus, pickleEvent.pickle.getLanguage(), scenarioResult, dryRun); after.run(this, bus, scenario, dryRun);
scenarioResult.add(stepResult);
} }


Long stopTime = bus.getTime(); Long stopTime = bus.getTime();
bus.send(new TestCaseFinished(stopTime, this, new Result(scenarioResult.getStatus(), stopTime - startTime, scenarioResult.getError()))); bus.send(new TestCaseFinished(stopTime, this, new Result(scenario.getStatus(), stopTime - startTime, scenario.getError())));
} }


@Override @Override
public List<TestStep> getTestSteps() { public List<TestStep> getTestSteps() {
List<TestStep> testSteps = new ArrayList<TestStep>(); List<TestStep> testSteps = new ArrayList<TestStep>(beforeHooks);
testSteps.addAll(beforeHooks);
for (PickleStepTestStep step : this.testSteps) { for (PickleStepTestStep step : this.testSteps) {
testSteps.addAll(step.getBeforeStepHookSteps()); testSteps.addAll(step.getBeforeStepHookSteps());
testSteps.add(step); testSteps.add(step);
Expand Down Expand Up @@ -91,6 +84,14 @@ public int getLine() {
return pickleEvent.pickle.getLocations().get(0).getLine(); return pickleEvent.pickle.getLocations().get(0).getLine();
} }


public List<Integer> getLines() {
List<Integer> lines = new ArrayList<>();
for (PickleLocation location : pickleEvent.pickle.getLocations()) {
lines.add(location.getLine());
}
return lines;
}

private String fileColonLine(PickleLocation location) { private String fileColonLine(PickleLocation location) {
return pickleEvent.uri + ":" + location.getLine(); return pickleEvent.uri + ":" + location.getLine();
} }
Expand Down
23 changes: 16 additions & 7 deletions core/src/main/java/cucumber/runner/TestStep.java
Expand Up @@ -2,7 +2,6 @@


import cucumber.api.Pending; import cucumber.api.Pending;
import cucumber.api.Result; import cucumber.api.Result;
import cucumber.api.Scenario;
import cucumber.api.TestCase; import cucumber.api.TestCase;
import cucumber.api.event.TestStepFinished; import cucumber.api.event.TestStepFinished;
import cucumber.api.event.TestStepStarted; import cucumber.api.event.TestStepStarted;
Expand Down Expand Up @@ -34,29 +33,39 @@ public String getCodeLocation() {
return stepDefinitionMatch.getCodeLocation(); return stepDefinitionMatch.getCodeLocation();
} }


Result run(TestCase testCase, EventBus bus, String language, Scenario scenario, boolean skipSteps) { /**
* Runs a test step.
*
* @param testCase
* @param bus
* @param scenario
* @param skipSteps
* @return true iff subsequent skippable steps should be skipped
*/
boolean run(TestCase testCase, EventBus bus, Scenario scenario, boolean skipSteps) {
Long startTime = bus.getTime(); Long startTime = bus.getTime();
bus.send(new TestStepStarted(startTime, testCase, this)); bus.send(new TestStepStarted(startTime, testCase, this));
Result.Type status; Result.Type status;
Throwable error = null; Throwable error = null;
try { try {
status = executeStep(language, scenario, skipSteps); status = executeStep(scenario, skipSteps);
} catch (Throwable t) { } catch (Throwable t) {
error = t; error = t;
status = mapThrowableToStatus(t); status = mapThrowableToStatus(t);
} }
Long stopTime = bus.getTime(); Long stopTime = bus.getTime();
Result result = mapStatusToResult(status, error, stopTime - startTime); Result result = mapStatusToResult(status, error, stopTime - startTime);
scenario.add(result);
bus.send(new TestStepFinished(stopTime, testCase, this, result)); bus.send(new TestStepFinished(stopTime, testCase, this, result));
return result; return !result.is(Result.Type.PASSED);
} }


private Result.Type executeStep(String language, Scenario scenario, boolean skipSteps) throws Throwable { private Result.Type executeStep(cucumber.api.Scenario scenario, boolean skipSteps) throws Throwable {
if (!skipSteps) { if (!skipSteps) {
stepDefinitionMatch.runStep(language, scenario); stepDefinitionMatch.runStep(scenario);
return Result.Type.PASSED; return Result.Type.PASSED;
} else { } else {
stepDefinitionMatch.dryRunStep(language, scenario); stepDefinitionMatch.dryRunStep(scenario);
return Result.Type.SKIPPED; return Result.Type.SKIPPED;
} }
} }
Expand Down
Expand Up @@ -15,13 +15,13 @@ public AmbiguousPickleStepDefinitionsMatch(String uri, PickleStep step, Ambiguou
} }


@Override @Override
public void runStep(String language, Scenario scenario) throws Throwable { public void runStep(Scenario scenario) {
throw exception; throw exception;
} }


@Override @Override
public void dryRunStep(String language, Scenario scenario) throws Throwable { public void dryRunStep(Scenario scenario) {
runStep(language, scenario); runStep(scenario);
} }


@Override @Override
Expand Down
@@ -1,8 +1,8 @@
package cucumber.runtime; package cucumber.runtime;


import io.cucumber.stepexpression.Argument;
import cucumber.api.Scenario; import cucumber.api.Scenario;
import gherkin.pickles.PickleStep; import gherkin.pickles.PickleStep;
import io.cucumber.stepexpression.Argument;


import java.util.Collections; import java.util.Collections;


Expand All @@ -15,13 +15,13 @@ public FailedPickleStepInstantiationMatch(String uri, PickleStep step, Throwable
} }


@Override @Override
public void runStep(String language, Scenario scenario) throws Throwable { public void runStep(Scenario scenario) throws Throwable {
throw throwable; throw throwable;
} }


@Override @Override
public void dryRunStep(String language, Scenario scenario) throws Throwable { public void dryRunStep(Scenario scenario) throws Throwable {
runStep(language, scenario); runStep(scenario);
} }


} }
4 changes: 2 additions & 2 deletions core/src/main/java/cucumber/runtime/HookDefinitionMatch.java
Expand Up @@ -10,12 +10,12 @@ public HookDefinitionMatch(HookDefinition hookDefinition) {
} }


@Override @Override
public void runStep(String language, Scenario scenario) throws Throwable { public void runStep(Scenario scenario) throws Throwable {
hookDefinition.execute(scenario); hookDefinition.execute(scenario);
} }


@Override @Override
public void dryRunStep(String language, Scenario scenario) throws Throwable { public void dryRunStep(Scenario scenario) {
// Do nothing // Do nothing
} }


Expand Down
4 changes: 2 additions & 2 deletions core/src/main/java/cucumber/runtime/NoStepDefinition.java
@@ -1,7 +1,7 @@
package cucumber.runtime; package cucumber.runtime;


import io.cucumber.stepexpression.Argument;
import gherkin.pickles.PickleStep; import gherkin.pickles.PickleStep;
import io.cucumber.stepexpression.Argument;


import java.util.List; import java.util.List;


Expand All @@ -23,7 +23,7 @@ public Integer getParameterCount() {
} }


@Override @Override
public void execute(String language, Object[] args) { public void execute(Object[] args) {
} }


@Override @Override
Expand Down

0 comments on commit 1b0680b

Please sign in to comment.