Skip to content

Commit f3c908c

Browse files
committed
Run after step hook until the next test step from a gherkin step
1 parent e0f3980 commit f3c908c

11 files changed

+207
-68
lines changed

core/src/main/java/cucumber/api/TestCase.java

+13-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ public class TestCase {
1414
private final PickleEvent pickleEvent;
1515
private final List<TestStep> testSteps;
1616
private final boolean dryRun;
17+
public enum SkipStatus {
18+
RUN_ALL,
19+
RUN_HOOKS,
20+
SKIP_ALL_SKIPABLE
21+
};
1722

1823
/**
1924
* Creates a new instance of a test case.
@@ -50,16 +55,21 @@ public TestCase(List<TestStep> testSteps, PickleEvent pickleEvent, boolean dryRu
5055
*/
5156
@Deprecated
5257
public void run(EventBus bus) {
53-
boolean skipNextStep = this.dryRun;
58+
TestCase.SkipStatus skipNextStep = this.dryRun ? SkipStatus.SKIP_ALL_SKIPABLE : SkipStatus.RUN_ALL;
5459
Long startTime = bus.getTime();
5560
bus.send(new TestCaseStarted(startTime, this));
5661
ScenarioImpl scenarioResult = new ScenarioImpl(bus, pickleEvent);
62+
boolean finishingGherkinStep = false;
5763
for (TestStep step : testSteps) {
64+
if (skipNextStep == SkipStatus.RUN_HOOKS && finishingGherkinStep && step.startingGherkinStepType()) {
65+
skipNextStep = SkipStatus.SKIP_ALL_SKIPABLE;
66+
}
5867
Result stepResult = step.run(bus, pickleEvent.pickle.getLanguage(), scenarioResult, skipNextStep);
59-
if (!stepResult.is(Result.Type.PASSED)) {
60-
skipNextStep = true;
68+
if (!stepResult.is(Result.Type.PASSED) && skipNextStep == SkipStatus.RUN_ALL) {
69+
skipNextStep = SkipStatus.RUN_HOOKS;
6170
}
6271
scenarioResult.add(stepResult);
72+
finishingGherkinStep = step.finishingGherkinStepType();
6373
}
6474
Long stopTime = bus.getTime();
6575
bus.send(new TestCaseFinished(stopTime, this, new Result(scenarioResult.getStatus(), stopTime - startTime, scenarioResult.getError())));

core/src/main/java/cucumber/api/TestStep.java

+15-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package cucumber.api;
22

3+
import cucumber.api.TestCase.SkipStatus;
34
import cucumber.api.event.TestStepFinished;
45
import cucumber.api.event.TestStepStarted;
56
import cucumber.runner.EventBus;
@@ -74,7 +75,7 @@ public List<cucumber.runtime.Argument> getDefinitionArgument() {
7475
* @deprecated not part of the public api
7576
*/
7677
@Deprecated
77-
public Result run(EventBus bus, String language, Scenario scenario, boolean skipSteps) {
78+
public Result run(EventBus bus, String language, Scenario scenario, TestCase.SkipStatus skipSteps) {
7879
Long startTime = bus.getTime();
7980
bus.send(new TestStepStarted(startTime, this));
8081
Result.Type status;
@@ -91,14 +92,25 @@ public Result run(EventBus bus, String language, Scenario scenario, boolean skip
9192
return result;
9293
}
9394

95+
96+
@Deprecated
97+
public boolean startingGherkinStepType() {
98+
return true;
99+
}
100+
101+
@Deprecated
102+
public boolean finishingGherkinStepType() {
103+
return true;
104+
}
105+
94106
@Deprecated
95107
protected Result.Type nonExceptionStatus(boolean skipSteps) {
96108
return skipSteps ? Result.Type.SKIPPED : Result.Type.PASSED;
97109
}
98110

99111
@Deprecated
100-
protected Result.Type executeStep(String language, Scenario scenario, boolean skipSteps) throws Throwable {
101-
if (!skipSteps) {
112+
protected Result.Type executeStep(String language, Scenario scenario, TestCase.SkipStatus skipSteps) throws Throwable {
113+
if (skipSteps == SkipStatus.RUN_ALL) {
102114
definitionMatch.runStep(language, scenario);
103115
return Result.Type.PASSED;
104116
} else {

core/src/main/java/cucumber/runner/HookStep.java

+11-17
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,21 @@
11
package cucumber.runner;
22

33
import cucumber.api.HookType;
4-
import cucumber.api.Result;
5-
import cucumber.api.Scenario;
64
import cucumber.api.TestStep;
75
import cucumber.runtime.DefinitionMatch;
86
import gherkin.pickles.Argument;
97
import gherkin.pickles.PickleStep;
108

119
import java.util.List;
1210

13-
public class HookStep extends TestStep {
11+
public abstract class HookStep extends TestStep {
1412
private final HookType hookType;
1513

1614
public HookStep(HookType hookType, DefinitionMatch definitionMatch) {
1715
super(definitionMatch);
1816
this.hookType = hookType;
1917
}
2018

21-
protected Result.Type executeStep(String language, Scenario scenario, boolean skipSteps) throws Throwable {
22-
if (hookType == HookType.After || hookType == HookType.Before) {
23-
definitionMatch.runStep(language, scenario);
24-
return Result.Type.PASSED;
25-
} else { //Either hook step is AfterStep or BeforeStep
26-
if (!skipSteps) {
27-
definitionMatch.runStep(language, scenario);
28-
return Result.Type.PASSED;
29-
} else {
30-
return Result.Type.SKIPPED;
31-
}
32-
}
33-
}
34-
3519
@Override
3620
public boolean isHook() {
3721
return true;
@@ -66,4 +50,14 @@ public List<Argument> getStepArgument() {
6650
public HookType getHookType() {
6751
return hookType;
6852
}
53+
54+
@Override
55+
public boolean startingGherkinStepType() {
56+
return false;
57+
}
58+
59+
@Override
60+
public boolean finishingGherkinStepType() {
61+
return hookType == HookType.Before || hookType == HookType.AfterStep;
62+
}
6963
}

core/src/main/java/cucumber/runner/Runner.java

+12-7
Original file line numberDiff line numberDiff line change
@@ -135,21 +135,26 @@ private void addTestStepsForPickleSteps(List<TestStep> testSteps, PickleEvent pi
135135
}
136136

137137
private void addTestStepsForBeforeHooks(List<TestStep> testSteps, List<PickleTag> tags) {
138-
addTestStepsForHooks(testSteps, tags, glue.getBeforeHooks(), HookType.Before);
138+
addTestStepsForScenarioHooks(testSteps, tags, glue.getBeforeHooks(), HookType.Before);
139139
}
140140

141141
private void addTestStepsForAfterHooks(List<TestStep> testSteps, List<PickleTag> tags) {
142-
addTestStepsForHooks(testSteps, tags, glue.getAfterHooks(), HookType.After);
142+
addTestStepsForScenarioHooks(testSteps, tags, glue.getAfterHooks(), HookType.After);
143143
}
144144

145-
private void addTestStepsForAfterStepHooks(List<TestStep> testSteps, List<PickleTag> tags) {
146-
addTestStepsForHooks(testSteps, tags, glue.getAfterStepHooks(), HookType.AfterStep);
145+
private void addTestStepsForScenarioHooks(List<TestStep> testSteps, List<PickleTag> tags, List<HookDefinition> hooks, HookType hookType) {
146+
for (HookDefinition hook : hooks) {
147+
if (hook.matches(tags)) {
148+
TestStep testStep = new UnskipableHookStep(hookType, new HookDefinitionMatch(hook));
149+
testSteps.add(testStep);
150+
}
151+
}
147152
}
148153

149-
private void addTestStepsForHooks(List<TestStep> testSteps, List<PickleTag> tags, List<HookDefinition> hooks, HookType hookType) {
150-
for (HookDefinition hook : hooks) {
154+
private void addTestStepsForAfterStepHooks(List<TestStep> testSteps, List<PickleTag> tags) {
155+
for (HookDefinition hook : glue.getAfterStepHooks()) {
151156
if (hook.matches(tags)) {
152-
TestStep testStep = new HookStep(hookType, new HookDefinitionMatch(hook));
157+
TestStep testStep = new SkipableHookStep(HookType.AfterStep, new HookDefinitionMatch(hook));
153158
testSteps.add(testStep);
154159
}
155160
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package cucumber.runner;
2+
3+
import cucumber.api.HookType;
4+
import cucumber.api.Result;
5+
import cucumber.api.Result.Type;
6+
import cucumber.api.Scenario;
7+
import cucumber.api.TestCase.SkipStatus;
8+
import cucumber.runtime.DefinitionMatch;
9+
10+
public class SkipableHookStep extends HookStep {
11+
12+
public SkipableHookStep(HookType hookType, DefinitionMatch definitionMatch) {
13+
super(hookType, definitionMatch);
14+
}
15+
16+
@Override
17+
protected Type executeStep(String language, Scenario scenario, SkipStatus skipSteps) throws Throwable {
18+
if (skipSteps != SkipStatus.SKIP_ALL_SKIPABLE) {
19+
definitionMatch.runStep(language, scenario);
20+
return Result.Type.PASSED;
21+
} else {
22+
definitionMatch.dryRunStep(language, scenario);
23+
return Result.Type.SKIPPED;
24+
}
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package cucumber.runner;
2+
3+
import cucumber.api.HookType;
4+
import cucumber.api.Result;
5+
import cucumber.api.Result.Type;
6+
import cucumber.api.Scenario;
7+
import cucumber.api.TestCase.SkipStatus;
8+
import cucumber.runtime.DefinitionMatch;
9+
10+
public class UnskipableHookStep extends HookStep {
11+
12+
public UnskipableHookStep(HookType hookType, DefinitionMatch definitionMatch) {
13+
super(hookType, definitionMatch);
14+
}
15+
16+
@Override
17+
protected Type executeStep(String language, Scenario scenario, SkipStatus skipSteps) throws Throwable {
18+
definitionMatch.runStep(language, scenario);
19+
return Result.Type.PASSED;
20+
}
21+
}

core/src/test/java/cucumber/api/TestCaseTest.java

+32-12
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
import java.util.Arrays;
1717

18-
import static org.mockito.Matchers.anyBoolean;
1918
import static org.mockito.Matchers.eq;
2019
import static org.mockito.Matchers.isA;
2120
import static org.mockito.Mockito.inOrder;
@@ -30,14 +29,14 @@ public void run_wraps_execute_in_test_case_started_and_finished_events() throws
3029
EventBus bus = mock(EventBus.class);
3130
String language = ENGLISH;
3231
TestStep testStep = mock(TestStep.class);
33-
when(testStep.run(eq(bus), eq(language), isA(Scenario.class), anyBoolean())).thenReturn(resultWithStatus(Result.Type.UNDEFINED));
32+
when(testStep.run(eq(bus), eq(language), isA(Scenario.class), isA(TestCase.SkipStatus.class))).thenReturn(resultWithStatus(Result.Type.UNDEFINED));
3433

3534
TestCase testCase = new TestCase(Arrays.asList(testStep), pickleEvent(), false);
3635
testCase.run(bus);
3736

3837
InOrder order = inOrder(bus, testStep);
3938
order.verify(bus).send(isA(TestCaseStarted.class));
40-
order.verify(testStep).run(eq(bus), eq(language), isA(Scenario.class), eq(false));
39+
order.verify(testStep).run(eq(bus), eq(language), isA(Scenario.class), eq(TestCase.SkipStatus.RUN_ALL));
4140
order.verify(bus).send(isA(TestCaseFinished.class));
4241
}
4342

@@ -46,33 +45,54 @@ public void run_all_steps() throws Throwable {
4645
EventBus bus = mock(EventBus.class);
4746
String language = ENGLISH;
4847
TestStep testStep1 = mock(TestStep.class);
49-
when(testStep1.run(eq(bus), eq(language), isA(Scenario.class), anyBoolean())).thenReturn(resultWithStatus(Result.Type.PASSED));
48+
when(testStep1.run(eq(bus), eq(language), isA(Scenario.class), isA(TestCase.SkipStatus.class))).thenReturn(resultWithStatus(Result.Type.PASSED));
5049
TestStep testStep2 = mock(TestStep.class);
51-
when(testStep2.run(eq(bus), eq(language), isA(Scenario.class), anyBoolean())).thenReturn(resultWithStatus(Result.Type.PASSED));
50+
when(testStep2.run(eq(bus), eq(language), isA(Scenario.class), isA(TestCase.SkipStatus.class))).thenReturn(resultWithStatus(Result.Type.PASSED));
5251

5352
TestCase testCase = new TestCase(Arrays.asList(testStep1, testStep2), pickleEvent(), false);
5453
testCase.run(bus);
5554

5655
InOrder order = inOrder(testStep1, testStep2);
57-
order.verify(testStep1).run(eq(bus), eq(language), isA(Scenario.class), eq(false));
58-
order.verify(testStep2).run(eq(bus), eq(language), isA(Scenario.class), eq(false));
56+
order.verify(testStep1).run(eq(bus), eq(language), isA(Scenario.class), eq(TestCase.SkipStatus.RUN_ALL));
57+
order.verify(testStep2).run(eq(bus), eq(language), isA(Scenario.class), eq(TestCase.SkipStatus.RUN_ALL));
5958
}
6059

6160
@Test
62-
public void skip_steps_after_the_first_non_passed_result() throws Throwable {
61+
public void run_hooks_after_the_first_non_passed_result_for_gherkin_step() throws Throwable {
6362
EventBus bus = mock(EventBus.class);
6463
String language = ENGLISH;
6564
TestStep testStep1 = mock(TestStep.class);
66-
when(testStep1.run(eq(bus), eq(language), isA(Scenario.class), anyBoolean())).thenReturn(resultWithStatus(Result.Type.UNDEFINED));
65+
when(testStep1.run(eq(bus), eq(language), isA(Scenario.class), isA(TestCase.SkipStatus.class))).thenReturn(resultWithStatus(Result.Type.UNDEFINED));
66+
when(testStep1.finishingGherkinStepType()).thenReturn(true);
6767
TestStep testStep2 = mock(TestStep.class);
68-
when(testStep2.run(eq(bus), eq(language), isA(Scenario.class), anyBoolean())).thenReturn(resultWithStatus(Result.Type.SKIPPED));
68+
when(testStep2.run(eq(bus), eq(language), isA(Scenario.class), isA(TestCase.SkipStatus.class))).thenReturn(resultWithStatus(Result.Type.SKIPPED));
69+
when(testStep2.startingGherkinStepType()).thenReturn(false);
6970

7071
TestCase testCase = new TestCase(Arrays.asList(testStep1, testStep2), pickleEvent(), false);
7172
testCase.run(bus);
7273

7374
InOrder order = inOrder(testStep1, testStep2);
74-
order.verify(testStep1).run(eq(bus), eq(language), isA(Scenario.class), eq(false));
75-
order.verify(testStep2).run(eq(bus), eq(language), isA(Scenario.class), eq(true));
75+
order.verify(testStep1).run(eq(bus), eq(language), isA(Scenario.class), eq(TestCase.SkipStatus.RUN_ALL));
76+
order.verify(testStep2).run(eq(bus), eq(language), isA(Scenario.class), eq(TestCase.SkipStatus.RUN_HOOKS));
77+
}
78+
79+
@Test
80+
public void skip_steps_at_first_gherkin_step_after_nonn_passed_result() throws Throwable {
81+
EventBus bus = mock(EventBus.class);
82+
String language = ENGLISH;
83+
TestStep testStep1 = mock(TestStep.class);
84+
when(testStep1.run(eq(bus), eq(language), isA(Scenario.class), isA(TestCase.SkipStatus.class))).thenReturn(resultWithStatus(Result.Type.UNDEFINED));
85+
when(testStep1.finishingGherkinStepType()).thenReturn(true);
86+
TestStep testStep2 = mock(TestStep.class);
87+
when(testStep2.run(eq(bus), eq(language), isA(Scenario.class), isA(TestCase.SkipStatus.class))).thenReturn(resultWithStatus(Result.Type.SKIPPED));
88+
when(testStep2.startingGherkinStepType()).thenReturn(true);
89+
90+
TestCase testCase = new TestCase(Arrays.asList(testStep1, testStep2), pickleEvent(), false);
91+
testCase.run(bus);
92+
93+
InOrder order = inOrder(testStep1, testStep2);
94+
order.verify(testStep1).run(eq(bus), eq(language), isA(Scenario.class), eq(TestCase.SkipStatus.RUN_ALL));
95+
order.verify(testStep2).run(eq(bus), eq(language), isA(Scenario.class), eq(TestCase.SkipStatus.SKIP_ALL_SKIPABLE));
7696
}
7797

7898
private PickleEvent pickleEvent() {

core/src/test/java/cucumber/api/TestStepTest.java

+9-9
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public class TestStepTest {
2828

2929
@Test
3030
public void run_wraps_run_step_in_test_step_started_and_finished_events() throws Throwable {
31-
step.run(bus, language, scenario, false);
31+
step.run(bus, language, scenario, TestCase.SkipStatus.RUN_ALL);
3232

3333
InOrder order = inOrder(bus, definitionMatch);
3434
order.verify(bus).send(isA(TestStepStarted.class));
@@ -38,7 +38,7 @@ public void run_wraps_run_step_in_test_step_started_and_finished_events() throws
3838

3939
@Test
4040
public void run_does_dry_run_step_when_skip_steps_is_true() throws Throwable {
41-
step.run(bus, language, scenario, true);
41+
step.run(bus, language, scenario, TestCase.SkipStatus.RUN_HOOKS);
4242

4343
InOrder order = inOrder(bus, definitionMatch);
4444
order.verify(bus).send(isA(TestStepStarted.class));
@@ -48,14 +48,14 @@ public void run_does_dry_run_step_when_skip_steps_is_true() throws Throwable {
4848

4949
@Test
5050
public void result_is_passed_when_step_definition_does_not_throw_exception() throws Throwable {
51-
Result result = step.run(bus, language, scenario, false);
51+
Result result = step.run(bus, language, scenario, TestCase.SkipStatus.RUN_ALL);
5252

5353
assertEquals(Result.Type.PASSED, result.getStatus());
5454
}
5555

5656
@Test
57-
public void result_is_skipped_when_skip_step_is_true() throws Throwable {
58-
Result result = step.run(bus, language, scenario, true);
57+
public void result_is_skipped_when_skip_step_is_not_run_all() throws Throwable {
58+
Result result = step.run(bus, language, scenario, TestCase.SkipStatus.RUN_HOOKS);
5959

6060
assertEquals(Result.Type.SKIPPED, result.getStatus());
6161
}
@@ -64,7 +64,7 @@ public void result_is_skipped_when_skip_step_is_true() throws Throwable {
6464
public void result_is_skipped_when_step_definition_throws_assumption_violated_exception() throws Throwable {
6565
doThrow(AssumptionViolatedException.class).when(definitionMatch).runStep(anyString(), (Scenario)any());
6666

67-
Result result = step.run(bus, language, scenario, false);
67+
Result result = step.run(bus, language, scenario, TestCase.SkipStatus.RUN_ALL);
6868

6969
assertEquals(Result.Type.SKIPPED, result.getStatus());
7070
}
@@ -73,7 +73,7 @@ public void result_is_skipped_when_step_definition_throws_assumption_violated_ex
7373
public void result_is_failed_when_step_definition_throws_exception() throws Throwable {
7474
doThrow(RuntimeException.class).when(definitionMatch).runStep(anyString(), (Scenario)any());
7575

76-
Result result = step.run(bus, language, scenario, false);
76+
Result result = step.run(bus, language, scenario, TestCase.SkipStatus.RUN_ALL);
7777

7878
assertEquals(Result.Type.FAILED, result.getStatus());
7979
}
@@ -82,7 +82,7 @@ public void result_is_failed_when_step_definition_throws_exception() throws Thro
8282
public void result_is_pending_when_step_definition_throws_pending_exception() throws Throwable {
8383
doThrow(PendingException.class).when(definitionMatch).runStep(anyString(), (Scenario)any());
8484

85-
Result result = step.run(bus, language, scenario, false);
85+
Result result = step.run(bus, language, scenario, TestCase.SkipStatus.RUN_ALL);
8686

8787
assertEquals(Result.Type.PENDING, result.getStatus());
8888
}
@@ -97,7 +97,7 @@ public void step_execution_time_is_measured() throws Throwable {
9797
.thenReturn(0L)
9898
.thenReturn(1234L);
9999

100-
Result result = step.run(bus, language, scenario, false);
100+
Result result = step.run(bus, language, scenario, TestCase.SkipStatus.RUN_ALL);
101101

102102
assertEquals(duration, result.getDuration());
103103
}

core/src/test/java/cucumber/runner/RunnerTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public void steps_are_skipped_after_failure() throws Throwable {
7171
}
7272

7373
@Test
74-
public void aftersteps_are_skipped_after_failure() throws Throwable {
74+
public void aftersteps_are_executed_after_failed_step() throws Throwable {
7575

7676
StepDefinition stepDefinition = mock(StepDefinition.class);
7777
doThrow(RuntimeException.class).when(stepDefinition).execute(Matchers.anyString(), Matchers.<Object[]>any());
@@ -81,7 +81,7 @@ public void aftersteps_are_skipped_after_failure() throws Throwable {
8181

8282
InOrder inOrder = inOrder(afteStepHook, stepDefinition);
8383
inOrder.verify(stepDefinition).execute(Matchers.anyString(), Matchers.<Object[]>any());
84-
inOrder.verify(afteStepHook, never()).execute(Matchers.<Scenario>any());
84+
inOrder.verify(afteStepHook).execute(Matchers.<Scenario>any());
8585
}
8686

8787
@Test

0 commit comments

Comments
 (0)