Skip to content

Commit 21f3abd

Browse files
committed
Hooks can receive results. Closes cucumber#27.
1 parent 958ab79 commit 21f3abd

File tree

11 files changed

+130
-36
lines changed

11 files changed

+130
-36
lines changed

core/src/main/java/cucumber/runtime/HookDefinition.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import java.util.Collection;
44

55
public interface HookDefinition {
6-
void execute() throws Throwable;
6+
void execute(ScenarioResult scenarioResult) throws Throwable;
77

88
boolean matches(Collection<String> tags);
99

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package cucumber.runtime;
2+
3+
import gherkin.formatter.model.Result;
4+
5+
import java.util.ArrayList;
6+
import java.util.List;
7+
8+
import static java.util.Arrays.asList;
9+
10+
/**
11+
* The aggregated result of the steps in a Scenario.
12+
*/
13+
public class ScenarioResult {
14+
private static final List<String> SEVERITY = asList("passed", "undefined", "pending", "skipped", "failed");
15+
private final List<Result> stepResults = new ArrayList<Result>();
16+
17+
void add(Result result) {
18+
stepResults.add(result);
19+
}
20+
21+
/**
22+
* @return one of: "passed", "undefined", "pending", "skipped", "failed"
23+
*/
24+
public String getStatus() {
25+
int pos = 0;
26+
for (Result stepResult : stepResults) {
27+
pos = Math.max(pos, SEVERITY.indexOf(stepResult.getStatus()));
28+
}
29+
return SEVERITY.get(pos);
30+
}
31+
32+
/**
33+
* @return true if {@link #getStatus()} returns "failed"
34+
*/
35+
public boolean isFailed() {
36+
return "failed".equals(getStatus());
37+
}
38+
}

core/src/main/java/cucumber/runtime/World.java

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public class World {
1515
private final Collection<String> tags;
1616

1717
private boolean skipNextStep = false;
18+
private ScenarioResult scenarioResult;
1819

1920
public World(List<Backend> backends, Runtime runtime, Collection<String> tags) {
2021
this.backends = backends;
@@ -23,35 +24,36 @@ public World(List<Backend> backends, Runtime runtime, Collection<String> tags) {
2324
}
2425

2526
public void prepare() {
26-
List<HookDefinition> hooks = new ArrayList<HookDefinition>();
27+
scenarioResult = new ScenarioResult();
28+
List<HookDefinition> beforeHooks = new ArrayList<HookDefinition>();
2729
for (Backend backend : backends) {
2830
backend.newWorld();
29-
hooks.addAll(backend.getBeforeHooks());
31+
beforeHooks.addAll(backend.getBeforeHooks());
3032
}
31-
Collections.sort(hooks, new HookComparator(true));
32-
for (HookDefinition hook : hooks) {
33-
runHookMaybe(hook);
33+
Collections.sort(beforeHooks, new HookComparator(true));
34+
for (HookDefinition hook : beforeHooks) {
35+
runHookMaybe(hook, null);
3436
}
3537
}
3638

3739
public void dispose() {
38-
List<HookDefinition> hooks = new ArrayList<HookDefinition>();
40+
List<HookDefinition> afterHooks = new ArrayList<HookDefinition>();
3941
for (Backend backend : backends) {
40-
hooks.addAll(backend.getAfterHooks());
42+
afterHooks.addAll(backend.getAfterHooks());
4143
}
42-
Collections.sort(hooks, new HookComparator(false));
43-
for (HookDefinition hook : hooks) {
44-
runHookMaybe(hook);
44+
Collections.sort(afterHooks, new HookComparator(false));
45+
for (HookDefinition hook : afterHooks) {
46+
runHookMaybe(hook, scenarioResult);
4547
}
4648
for (Backend backend : backends) {
4749
backend.disposeWorld();
4850
}
4951
}
5052

51-
private void runHookMaybe(HookDefinition hook) {
53+
private void runHookMaybe(HookDefinition hook, ScenarioResult scenarioResult) {
5254
if (hook.matches(tags)) {
5355
try {
54-
hook.execute();
56+
hook.execute(scenarioResult);
5557
} catch (Throwable t) {
5658
skipNextStep = true;
5759
throw new CucumberException("Hook execution failed", t);
@@ -70,6 +72,7 @@ public void runStep(String uri, Step step, Reporter reporter, Locale locale) {
7072

7173
if (skipNextStep) {
7274
// Undefined steps (Match.NONE) will always get the Result.SKIPPED result
75+
scenarioResult.add(Result.SKIPPED);
7376
reporter.result(Result.SKIPPED);
7477
} else {
7578
Throwable e = null;
@@ -83,6 +86,7 @@ public void runStep(String uri, Step step, Reporter reporter, Locale locale) {
8386
long duration = System.nanoTime() - start;
8487
String status = e == null ? Result.PASSED : Result.FAILED;
8588
Result result = new Result(status, duration, e, DUMMY_ARG);
89+
scenarioResult.add(result);
8690
reporter.result(result);
8791
}
8892
}

core/src/test/java/cucumber/runtime/HookOrderTest.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ public void before_hooks_execute_in_order() throws Throwable {
3030
world.prepare();
3131

3232
InOrder inOrder = inOrder(hooks.toArray());
33-
inOrder.verify(hooks.get(2)).execute();
34-
inOrder.verify(hooks.get(0)).execute();
35-
inOrder.verify(hooks.get(1)).execute();
33+
inOrder.verify(hooks.get(2)).execute(null);
34+
inOrder.verify(hooks.get(0)).execute(null);
35+
inOrder.verify(hooks.get(1)).execute(null);
3636
}
3737

3838
@Test
@@ -43,9 +43,9 @@ public void after_hooks_execute_in_reverse_order() throws Throwable {
4343
world.dispose();
4444

4545
InOrder inOrder = inOrder(hooks.toArray());
46-
inOrder.verify(hooks.get(1)).execute();
47-
inOrder.verify(hooks.get(2)).execute();
48-
inOrder.verify(hooks.get(0)).execute();
46+
inOrder.verify(hooks.get(1)).execute(null);
47+
inOrder.verify(hooks.get(2)).execute(null);
48+
inOrder.verify(hooks.get(0)).execute(null);
4949
}
5050

5151
@Test
@@ -62,12 +62,12 @@ public void hooks_order_across_many_backends() throws Throwable {
6262
allHooks.addAll(backend2Hooks);
6363

6464
InOrder inOrder = inOrder(allHooks.toArray());
65-
inOrder.verify(backend1Hooks.get(2)).execute();
66-
inOrder.verify(backend2Hooks.get(0)).execute();
67-
inOrder.verify(backend1Hooks.get(0)).execute();
68-
inOrder.verify(backend2Hooks.get(2)).execute();
69-
verify(backend2Hooks.get(1)).execute();
70-
verify(backend1Hooks.get(1)).execute();
65+
inOrder.verify(backend1Hooks.get(2)).execute(null);
66+
inOrder.verify(backend2Hooks.get(0)).execute(null);
67+
inOrder.verify(backend1Hooks.get(0)).execute(null);
68+
inOrder.verify(backend2Hooks.get(2)).execute(null);
69+
verify(backend2Hooks.get(1)).execute(null);
70+
verify(backend1Hooks.get(1)).execute(null);
7171
}
7272

7373
private List<HookDefinition> mockHooks(int... ordering) {

core/src/test/java/cucumber/runtime/HookTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public void after_hooks_execute_before_objects_are_disposed() throws Throwable {
3232
world.dispose();
3333

3434
InOrder inOrder = inOrder(hook, backend);
35-
inOrder.verify(hook).execute();
35+
inOrder.verify(hook).execute(null);
3636
inOrder.verify(backend).disposeWorld();
3737
}
3838

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package cucumber.runtime;
2+
3+
import gherkin.formatter.model.Result;
4+
import org.junit.Test;
5+
6+
import static org.junit.Assert.assertEquals;
7+
8+
public class ScenarioResultTest {
9+
10+
private ScenarioResult r = new ScenarioResult();
11+
12+
@Test
13+
public void no_steps_is_passed() throws Exception {
14+
assertEquals("passed", r.getStatus());
15+
}
16+
17+
@Test
18+
public void passed_and_failed_is_passed() throws Exception {
19+
r.add(new Result("passed", 0L, null, null));
20+
r.add(new Result("failed", 0L, null, null));
21+
assertEquals("failed", r.getStatus());
22+
}
23+
24+
@Test
25+
public void passed_and_skipped_is_skipped_although_we_cant_have_skipped_without_undefined_or_pending() throws Exception {
26+
r.add(new Result("passed", 0L, null, null));
27+
r.add(new Result("skipped", 0L, null, null));
28+
assertEquals("skipped", r.getStatus());
29+
}
30+
31+
@Test
32+
public void undefined_and_pending_is_pending() throws Exception {
33+
r.add(new Result("undefined", 0L, null, null));
34+
r.add(new Result("pending", 0L, null, null));
35+
assertEquals("pending", r.getStatus());
36+
}
37+
}

java/src/main/java/cucumber/runtime/java/JavaHookDefinition.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import cucumber.runtime.CucumberException;
44
import cucumber.runtime.HookDefinition;
5+
import cucumber.runtime.ScenarioResult;
56
import gherkin.TagExpression;
67

78
import java.lang.reflect.Method;
@@ -13,25 +14,31 @@ public class JavaHookDefinition implements HookDefinition {
1314

1415
private final ObjectFactory objectFactory;
1516
private final Method method;
16-
private final TagExpression tagExpression;
1717
private final int order;
18+
private final TagExpression tagExpression;
1819

1920
public JavaHookDefinition(Method method, String[] tagExpressions, int order, ObjectFactory objectFactory) {
2021
this.method = method;
2122
tagExpression = new TagExpression(asList(tagExpressions));
22-
this.objectFactory = objectFactory;
2323
this.order = order;
24+
this.objectFactory = objectFactory;
2425
}
2526

2627
Method getMethod() {
2728
return method;
2829
}
2930

3031
@Override
31-
public void execute() throws Throwable {
32+
public void execute(ScenarioResult scenarioResult) throws Throwable {
3233
Object target = objectFactory.getInstance(method.getDeclaringClass());
34+
Object[] args;
35+
if(method.getParameterTypes().length == 1) {
36+
args = new Object[]{scenarioResult};
37+
} else {
38+
args = new Object[0];
39+
}
3340
try {
34-
method.invoke(target);
41+
method.invoke(target, args);
3542
} catch (IllegalArgumentException e) {
3643
throw new CucumberException("Can't invoke "
3744
+ new MethodFormat().format(method));

picocontainer/src/test/java/cucumber/runtime/java/picocontainer/PicoFactoryTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import static org.junit.Assert.assertNotSame;
99

1010
public class PicoFactoryTest {
11-
@Ignore("Needs to be fixed")
1211
@Test
1312
public void shouldGiveUsNewInstancesForEachScenario() {
1413
ObjectFactory factory = new PicoFactory();

picocontainer/src/test/java/cucumber/runtime/java/picocontainer/StepDefs.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package cucumber.runtime.java.picocontainer;
22

3+
import cucumber.annotation.After;
34
import cucumber.annotation.en.Given;
45
import cucumber.annotation.en.Then;
6+
import cucumber.runtime.ScenarioResult;
57

68
import static junit.framework.Assert.assertEquals;
79

@@ -27,4 +29,11 @@ public void containerContainsIngredient(String container, String ingredient) thr
2729
public void addLiquid(String liquid) throws InterruptedException {
2830
assertEquals("milk", liquid);
2931
}
32+
33+
@After
34+
public void letsSeeWhatHappened(ScenarioResult result) {
35+
if(result.isFailed()) {
36+
// Maybe take a screenshot!
37+
}
38+
}
3039
}

scala/src/main/scala/cucumber/runtime/ScalaHookDefinition.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import _root_.java.util.Collection
88
class ScalaHookDefinition(f:() => Unit, order:Int, tags:Seq[String]) extends HookDefinition {
99
val tagExpression = new TagExpression(tags.asJava)
1010

11-
def execute() { f() }
11+
def execute(scenarioResult: ScenarioResult) { f() }
1212

1313
def matches(tags: Collection[String]) = tagExpression.eval(tags)
1414

0 commit comments

Comments
 (0)