From ffde0af9325c76a9be6530b35a8a418a7377a5bb Mon Sep 17 00:00:00 2001 From: Paul Warren Date: Thu, 16 Mar 2017 10:50:44 -0700 Subject: [PATCH] Improve error handling when execution blocks throw exceptions - Fixes [Exception in Context -> Tests Ignored #6](https://github.com/paulcwarren/ginkgo4j/issues/6) --- .../paulcwarren/ginkgo4j/ExecutableBlock.java | 2 +- .../paulcwarren/ginkgo4j/Ginkgo4jDSL.java | 97 +++++++++---- .../paulcwarren/ginkgo4j/Ginkgo4jRunner.java | 29 ++-- .../ginkgo4j/builder/TestVisitor.java | 10 +- .../ginkgo4j/builder/TestWalker.java | 10 +- .../ginkgo4j/chains/SpecsCollector.java | 14 +- .../paulcwarren/ginkgo4j/ExampleTests.java | 25 +++- .../ginkgo4j/Ginkgo4jRunnerTests.java | 127 +++++++++++++++++ .../ginkgo4j/NPEThrowingTestClass.java | 34 +++++ .../Ginkgo4jSpringApplicationTests.java | 1 + .../ginkgo4j/builder/SpecCollectorTests.java | 129 +++++++++++++++++- 11 files changed, 419 insertions(+), 59 deletions(-) create mode 100644 src/test/java/com/github/paulcwarren/ginkgo4j/NPEThrowingTestClass.java diff --git a/src/main/java/com/github/paulcwarren/ginkgo4j/ExecutableBlock.java b/src/main/java/com/github/paulcwarren/ginkgo4j/ExecutableBlock.java index 25091be..6016993 100644 --- a/src/main/java/com/github/paulcwarren/ginkgo4j/ExecutableBlock.java +++ b/src/main/java/com/github/paulcwarren/ginkgo4j/ExecutableBlock.java @@ -1,5 +1,5 @@ package com.github.paulcwarren.ginkgo4j; public interface ExecutableBlock { - void invoke() throws Throwable; + void invoke() throws Exception; } diff --git a/src/main/java/com/github/paulcwarren/ginkgo4j/Ginkgo4jDSL.java b/src/main/java/com/github/paulcwarren/ginkgo4j/Ginkgo4jDSL.java index 5090bc8..c6b94c0 100644 --- a/src/main/java/com/github/paulcwarren/ginkgo4j/Ginkgo4jDSL.java +++ b/src/main/java/com/github/paulcwarren/ginkgo4j/Ginkgo4jDSL.java @@ -1,87 +1,138 @@ package com.github.paulcwarren.ginkgo4j; +import java.util.Stack; + import impl.com.github.paulcwarren.ginkgo4j.builder.TestVisitor; public class Ginkgo4jDSL { - private static TestVisitor visitor = null; + private static Stack stack = new Stack<>(); public static synchronized void setVisitor(TestVisitor b) { - visitor = b; + stack.push(b); } public static synchronized void unsetVisitor(TestVisitor b) { - if (b != visitor) { + if (b != stack.peek()) { throw new IllegalStateException("Mismatched set/unset builder calls"); } - visitor = null; + stack.pop(); } public static void Describe(String text, ExecutableBlock block) { assertVisitor("Describe"); - if (visitor != null) { - visitor.describe(text, block, false); + if (stack.peek() != null) { + try { + stack.peek().describe(text, block, false); + } catch (Exception e) { + if (e instanceof RuntimeException) { + throw (RuntimeException)e; + } + throw new RuntimeException("Unable to execute test", e); + } } } public static void FDescribe(String text, ExecutableBlock block) { assertVisitor("FDescribe"); - if (visitor != null) { - visitor.describe(text, block, true); + if (stack.peek() != null) { + try { + stack.peek().describe(text, block, true); + } catch (Throwable e) { + if (e instanceof RuntimeException) { + throw (RuntimeException)e; + } + throw new RuntimeException("Unable to execute test", e); + } } } public static void Context(String text, ExecutableBlock block) { assertVisitor("Context"); - if (visitor != null) { - visitor.context(text, block, false); + if (stack.peek() != null) { + try { + stack.peek().context(text, block, false); + } catch (Throwable e) { + if (e instanceof RuntimeException) { + throw (RuntimeException)e; + } + throw new RuntimeException("Unable to execute test", e); + } } } public static void FContext(String text, ExecutableBlock block) { assertVisitor("FContext"); - if (visitor != null) { - visitor.context(text, block, true); + if (stack.peek() != null) { + try { + stack.peek().context(text, block, true); + } catch (Exception e) { + if (e instanceof RuntimeException) { + throw (RuntimeException)e; + } + throw new RuntimeException("Unable to execute test", e); + } } } public static void BeforeEach(ExecutableBlock block) { assertVisitor("BeforeEach"); - if (visitor != null) { - visitor.beforeEach(block); + if (stack.peek() != null) { + try { + stack.peek().beforeEach(block); + } catch (Exception e) { + if (e instanceof RuntimeException) { + throw (RuntimeException)e; + } + throw new RuntimeException("Unable to execute test", e); + } } } public static void JustBeforeEach(ExecutableBlock block) { assertVisitor("JustBeforeEach"); - if (visitor != null) { - visitor.justBeforeEach(block); + if (stack.peek() != null) { + try { + stack.peek().justBeforeEach(block); + } catch (Exception e) { + if (e instanceof RuntimeException) { + throw (RuntimeException)e; + } + throw new RuntimeException("Unable to execute test", e); + } } } public static void It(String text, ExecutableBlock block) { assertVisitor("It"); - if (visitor != null) { - visitor.it(text, block, false); + if (stack.peek() != null) { + stack.peek().it(text, block, false); } } public static void FIt(String text, ExecutableBlock block) { assertVisitor("FIt"); - if (visitor != null) { - visitor.it(text, block, true); + if (stack.peek() != null) { + stack.peek().it(text, block, true); } } public static void AfterEach(ExecutableBlock block) { assertVisitor("AfterEach"); - if (visitor != null) { - visitor.afterEach(block); + if (stack.peek() != null) { + try { + stack.peek().afterEach(block); + } catch (Exception e) { + if (e instanceof RuntimeException) { + throw (RuntimeException)e; + } + throw new RuntimeException("Unable to execute test", e); + } } } private static void assertVisitor(String string) { - if (visitor == null) { + if (stack.peek() == null) { throw new IllegalStateException(string); } } diff --git a/src/main/java/com/github/paulcwarren/ginkgo4j/Ginkgo4jRunner.java b/src/main/java/com/github/paulcwarren/ginkgo4j/Ginkgo4jRunner.java index 6cc5356..7fdae1f 100644 --- a/src/main/java/com/github/paulcwarren/ginkgo4j/Ginkgo4jRunner.java +++ b/src/main/java/com/github/paulcwarren/ginkgo4j/Ginkgo4jRunner.java @@ -10,6 +10,7 @@ import org.junit.runner.Description; import org.junit.runner.Runner; +import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunNotifier; import org.junit.runners.model.InitializationError; @@ -40,8 +41,12 @@ public Description getDescription() { description = Description.createSuiteDescription(testClass.getName(), (Annotation[])null); JunitDescriptionsCollector descCollector = new JunitDescriptionsCollector(description); - new TestWalker(testClass).walk(descCollector); - descriptions = descCollector.getDescriptions(); + // collect as many descriptions as we can + try { + new TestWalker(testClass).walk(descCollector); + } finally { + descriptions = descCollector.getDescriptions(); + } } return description; @@ -53,12 +58,20 @@ public void run(RunNotifier notifier) { // to ensure setup happens this.getDescription(); - List chains = calculateExecutionChains(testClass); - - RunnerListener listener = new JunitRunnerListener(notifier, descriptions); - List runners = calculateWorkerThreads(chains, listener); - - threadExecute(runners, getThreads(testClass)); + try { + notifier.fireTestStarted(description); + + List chains = calculateExecutionChains(testClass); + + RunnerListener listener = new JunitRunnerListener(notifier, descriptions); + List runners = calculateWorkerThreads(chains, listener); + + threadExecute(runners, getThreads(testClass)); + } catch (Exception e) { + notifier.fireTestFailure(new Failure(description, e)); + } finally { + notifier.fireTestFinished(description); + } } static List calculateExecutionChains(Class testClass) { diff --git a/src/main/java/impl/com/github/paulcwarren/ginkgo4j/builder/TestVisitor.java b/src/main/java/impl/com/github/paulcwarren/ginkgo4j/builder/TestVisitor.java index 285f7a3..5c515f0 100755 --- a/src/main/java/impl/com/github/paulcwarren/ginkgo4j/builder/TestVisitor.java +++ b/src/main/java/impl/com/github/paulcwarren/ginkgo4j/builder/TestVisitor.java @@ -5,11 +5,11 @@ public interface TestVisitor { void test(Object test); - void describe(String text, ExecutableBlock block, boolean isFocused); - void context(String text, ExecutableBlock block, boolean isFocused); - void beforeEach(ExecutableBlock block); - void justBeforeEach(ExecutableBlock block); + void describe(String text, ExecutableBlock block, boolean isFocused) throws Exception; + void context(String text, ExecutableBlock block, boolean isFocused) throws Exception; + void beforeEach(ExecutableBlock block) throws Exception; + void justBeforeEach(ExecutableBlock block) throws Exception; void it(String text, ExecutableBlock block, boolean isFocused); - void afterEach(ExecutableBlock block); + void afterEach(ExecutableBlock block) throws Exception; } diff --git a/src/main/java/impl/com/github/paulcwarren/ginkgo4j/builder/TestWalker.java b/src/main/java/impl/com/github/paulcwarren/ginkgo4j/builder/TestWalker.java index 27fd292..9a186bd 100755 --- a/src/main/java/impl/com/github/paulcwarren/ginkgo4j/builder/TestWalker.java +++ b/src/main/java/impl/com/github/paulcwarren/ginkgo4j/builder/TestWalker.java @@ -53,28 +53,28 @@ public void walk(TestVisitor...visitors) { } @Override - public void describe(String text, ExecutableBlock block, boolean isFocused) { + public void describe(String text, ExecutableBlock block, boolean isFocused) throws Exception { for (TestVisitor visitor : visitors) { visitor.describe(text, block, isFocused); } } @Override - public void context(String text, ExecutableBlock block, boolean isFocused) { + public void context(String text, ExecutableBlock block, boolean isFocused) throws Exception { for (TestVisitor visitor : visitors) { visitor.context(text, block, isFocused); } } @Override - public void beforeEach(ExecutableBlock block) { + public void beforeEach(ExecutableBlock block) throws Exception { for (TestVisitor visitor : visitors) { visitor.beforeEach(block); } } @Override - public void justBeforeEach(ExecutableBlock block) { + public void justBeforeEach(ExecutableBlock block) throws Exception { for (TestVisitor visitor : visitors) { visitor.justBeforeEach(block); } @@ -88,7 +88,7 @@ public void it(String text, ExecutableBlock block, boolean isFocused) { } @Override - public void afterEach(ExecutableBlock block) { + public void afterEach(ExecutableBlock block) throws Exception { for (TestVisitor visitor : visitors) { visitor.afterEach(block); } diff --git a/src/main/java/impl/com/github/paulcwarren/ginkgo4j/chains/SpecsCollector.java b/src/main/java/impl/com/github/paulcwarren/ginkgo4j/chains/SpecsCollector.java index 7a7eafa..dcaeb55 100755 --- a/src/main/java/impl/com/github/paulcwarren/ginkgo4j/chains/SpecsCollector.java +++ b/src/main/java/impl/com/github/paulcwarren/ginkgo4j/chains/SpecsCollector.java @@ -19,36 +19,32 @@ public List getSpecs() { return specs; } - public void describe(String text, ExecutableBlock block, boolean isFocused) { + public void describe(String text, ExecutableBlock block, boolean isFocused) throws Exception { text = IdBuilder.id(text); context.push(text); try { block.invoke(); - } catch (Throwable e) { - e.printStackTrace(); } finally { context.pop(); } } - public void context(String text, ExecutableBlock block, boolean isFocused) { + public void context(String text, ExecutableBlock block, boolean isFocused) throws Exception { text = IdBuilder.id(text); context.push(text); try { block.invoke(); - } catch (Throwable e) { - e.printStackTrace(); } finally { context.pop(); } } - public void beforeEach(ExecutableBlock block) { + public void beforeEach(ExecutableBlock block) throws Exception { } - public void justBeforeEach(ExecutableBlock block) { + public void justBeforeEach(ExecutableBlock block) throws Exception { } public void it(String text, ExecutableBlock block, boolean isFocused) { @@ -58,7 +54,7 @@ public void it(String text, ExecutableBlock block, boolean isFocused) { specs.add(spec); } - public void afterEach(ExecutableBlock block) { + public void afterEach(ExecutableBlock block) throws Exception { } @Override diff --git a/src/test/java/com/github/paulcwarren/ginkgo4j/ExampleTests.java b/src/test/java/com/github/paulcwarren/ginkgo4j/ExampleTests.java index 2b314aa..7f7f995 100644 --- a/src/test/java/com/github/paulcwarren/ginkgo4j/ExampleTests.java +++ b/src/test/java/com/github/paulcwarren/ginkgo4j/ExampleTests.java @@ -14,22 +14,33 @@ import junit.framework.Assert; @Ignore +@SuppressWarnings("deprecation") @RunWith(Ginkgo4jRunner.class) //@Ginkgo4jConfiguration(threads=1) public class ExampleTests { - private Example example; + private String something; + private Example example; { - Describe("", () -> { - Context("", () -> { - It("", () -> { + Describe("describe", () -> { + Context("context", () -> { + something.length(); + It("a test", () -> { assertThat(true, is(true)); }); }); - }); - - Describe("A describe", () -> { + }); + + Describe("", () -> { + Context("", () -> { + It("", () -> { + assertThat(true, is(true)); + }); + }); + }); + + Describe("A describe", () -> { BeforeEach(() -> { example = new Example(); diff --git a/src/test/java/com/github/paulcwarren/ginkgo4j/Ginkgo4jRunnerTests.java b/src/test/java/com/github/paulcwarren/ginkgo4j/Ginkgo4jRunnerTests.java index 9758029..5aac1fe 100644 --- a/src/test/java/com/github/paulcwarren/ginkgo4j/Ginkgo4jRunnerTests.java +++ b/src/test/java/com/github/paulcwarren/ginkgo4j/Ginkgo4jRunnerTests.java @@ -7,16 +7,24 @@ import static com.github.paulcwarren.ginkgo4j.Ginkgo4jDSL.FDescribe; import static com.github.paulcwarren.ginkgo4j.Ginkgo4jDSL.FIt; import static com.github.paulcwarren.ginkgo4j.Ginkgo4jDSL.It; +import static com.github.paulcwarren.ginkgo4j.Ginkgo4jDSL.JustBeforeEach; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import java.util.List; import org.junit.runner.RunWith; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunNotifier; +import org.mockito.ArgumentCaptor; import impl.com.github.paulcwarren.ginkgo4j.Spec; import impl.com.github.paulcwarren.ginkgo4j.chains.ExecutableChain; @@ -30,8 +38,127 @@ public class Ginkgo4jRunnerTests { private Ginkgo4jRunner runner; + private Throwable t; + + //mocks + private RunNotifier notifier; + List specs; { + Describe("#run", () -> { + JustBeforeEach(() -> { + runner.run(notifier); + }); + BeforeEach(() -> { + notifier = mock(RunNotifier.class); + }); + Context("when called with a passing test", () -> { + BeforeEach(() -> { + runner = new Ginkgo4jRunner(TestClass.class); + }); + It("should execute sucessfully", () -> { + assertThat(t, is(nullValue())); + verify(notifier, times(4)).fireTestStarted(anyObject()); + verify(notifier, times(4)).fireTestFinished(anyObject()); + }); + }); + Context("when called with a test whose Describe block throws an NPE", () -> { + BeforeEach(() -> { + NPEThrowingTestClass.describe = null; + NPEThrowingTestClass.context = ""; + NPEThrowingTestClass.jbe = ""; + NPEThrowingTestClass.be = ""; + NPEThrowingTestClass.ae = ""; + runner = new Ginkgo4jRunner(NPEThrowingTestClass.class); + }); + It("should report failure to junit's notifier", () -> { + verify(notifier, times(1)).fireTestStarted(anyObject()); + + ArgumentCaptor failureCaptor = ArgumentCaptor.forClass(Failure.class); + verify(notifier, times(1)).fireTestFailure(failureCaptor.capture()); + assertThat(failureCaptor.getValue().getException().getStackTrace()[0].getLineNumber(), is(18)); + + verify(notifier, times(1)).fireTestFinished(anyObject()); + }); + }); + Context("when called with a test whose BeforeEach block throws an NPE", () -> { + BeforeEach(() -> { + NPEThrowingTestClass.describe = ""; + NPEThrowingTestClass.context = ""; + NPEThrowingTestClass.jbe = ""; + NPEThrowingTestClass.be = null; + NPEThrowingTestClass.ae = ""; + runner = new Ginkgo4jRunner(NPEThrowingTestClass.class); + }); + It("should report failure to junit's notifier", () -> { + verify(notifier, times(2)).fireTestStarted(anyObject()); + + ArgumentCaptor failureCaptor = ArgumentCaptor.forClass(Failure.class); + verify(notifier, times(1)).fireTestFailure(failureCaptor.capture()); + assertThat(failureCaptor.getValue().getException().getStackTrace()[0].getLineNumber(), is(20)); + + verify(notifier, times(2)).fireTestFinished(anyObject()); + }); + }); + Context("when called with a test whose JustBeforeEach block throws an NPE", () -> { + BeforeEach(() -> { + NPEThrowingTestClass.describe = ""; + NPEThrowingTestClass.context = ""; + NPEThrowingTestClass.jbe = null; + NPEThrowingTestClass.be = ""; + NPEThrowingTestClass.ae = ""; + runner = new Ginkgo4jRunner(NPEThrowingTestClass.class); + }); + It("should report failure to junit's notifier", () -> { + verify(notifier, times(2)).fireTestStarted(anyObject()); + + ArgumentCaptor failureCaptor = ArgumentCaptor.forClass(Failure.class); + verify(notifier, times(1)).fireTestFailure(failureCaptor.capture()); + assertThat(failureCaptor.getValue().getException().getStackTrace()[0].getLineNumber(), is(23)); + + verify(notifier, times(2)).fireTestFinished(anyObject()); + }); + }); + Context("when called with a test whose Context block throws an NPE", () -> { + BeforeEach(() -> { + NPEThrowingTestClass.describe = ""; + NPEThrowingTestClass.context = null; + NPEThrowingTestClass.jbe = ""; + NPEThrowingTestClass.be = ""; + NPEThrowingTestClass.ae = ""; + runner = new Ginkgo4jRunner(NPEThrowingTestClass.class); + }); + It("should report failure to junit's notifier", () -> { + verify(notifier, times(1)).fireTestStarted(anyObject()); + + ArgumentCaptor failureCaptor = ArgumentCaptor.forClass(Failure.class); + verify(notifier, times(1)).fireTestFailure(failureCaptor.capture()); + assertThat(failureCaptor.getValue().getException().getStackTrace()[0].getLineNumber(), is(26)); + + verify(notifier, times(1)).fireTestFinished(anyObject()); + }); + }); + Context("when called with a test whose AfterEach block throws an NPE", () -> { + BeforeEach(() -> { + NPEThrowingTestClass.describe = ""; + NPEThrowingTestClass.context = ""; + NPEThrowingTestClass.jbe = ""; + NPEThrowingTestClass.be = ""; + NPEThrowingTestClass.ae = null; + runner = new Ginkgo4jRunner(NPEThrowingTestClass.class); + }); + It("should report failure to junit's notifier", () -> { + verify(notifier, times(2)).fireTestStarted(anyObject()); + + ArgumentCaptor failureCaptor = ArgumentCaptor.forClass(Failure.class); + verify(notifier, times(1)).fireTestFailure(failureCaptor.capture()); + assertThat(failureCaptor.getValue().getException().getStackTrace()[0].getLineNumber(), is(30)); + + verify(notifier, times(2)).fireTestFinished(anyObject()); + }); + }); + }); + Describe("#calculateExecutionChains", () -> { Context("when called with no focussed specs", () -> { BeforeEach(() -> { diff --git a/src/test/java/com/github/paulcwarren/ginkgo4j/NPEThrowingTestClass.java b/src/test/java/com/github/paulcwarren/ginkgo4j/NPEThrowingTestClass.java new file mode 100644 index 0000000..92fb6b3 --- /dev/null +++ b/src/test/java/com/github/paulcwarren/ginkgo4j/NPEThrowingTestClass.java @@ -0,0 +1,34 @@ +package com.github.paulcwarren.ginkgo4j; + +import static com.github.paulcwarren.ginkgo4j.Ginkgo4jDSL.AfterEach; +import static com.github.paulcwarren.ginkgo4j.Ginkgo4jDSL.BeforeEach; +import static com.github.paulcwarren.ginkgo4j.Ginkgo4jDSL.Context; +import static com.github.paulcwarren.ginkgo4j.Ginkgo4jDSL.Describe; +import static com.github.paulcwarren.ginkgo4j.Ginkgo4jDSL.It; +import static com.github.paulcwarren.ginkgo4j.Ginkgo4jDSL.JustBeforeEach; + +public class NPEThrowingTestClass { + public static String describe = ""; + public static String context = ""; + public static String jbe = ""; + public static String be = ""; + public static String ae = ""; + { + Describe("a describe", () -> { + describe.length(); + BeforeEach(() -> { + be.length(); + }); + JustBeforeEach(() -> { + jbe.length(); + }); + Context("a context", () -> { + context.length(); + It("stuff", () -> {}); + }); + AfterEach(() -> { + ae.length(); + }); + }); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/paulcwarren/ginkgo4j/spring/Ginkgo4jSpringApplicationTests.java b/src/test/java/com/github/paulcwarren/ginkgo4j/spring/Ginkgo4jSpringApplicationTests.java index ba72e21..23a6531 100644 --- a/src/test/java/com/github/paulcwarren/ginkgo4j/spring/Ginkgo4jSpringApplicationTests.java +++ b/src/test/java/com/github/paulcwarren/ginkgo4j/spring/Ginkgo4jSpringApplicationTests.java @@ -16,6 +16,7 @@ import com.github.paulcwarren.ginkgo4j.Ginkgo4jSpringRunner; import com.github.paulcwarren.ginkgo4j.spring.Ginkgo4jSpringApplication.HelloService; +@SuppressWarnings("deprecation") @RunWith(Ginkgo4jSpringRunner.class) @SpringApplicationConfiguration(classes = Ginkgo4jSpringApplication.class) public class Ginkgo4jSpringApplicationTests { diff --git a/src/test/java/impl/com/github/paulcwarren/ginkgo4j/builder/SpecCollectorTests.java b/src/test/java/impl/com/github/paulcwarren/ginkgo4j/builder/SpecCollectorTests.java index d126825..9ae473d 100755 --- a/src/test/java/impl/com/github/paulcwarren/ginkgo4j/builder/SpecCollectorTests.java +++ b/src/test/java/impl/com/github/paulcwarren/ginkgo4j/builder/SpecCollectorTests.java @@ -7,6 +7,7 @@ import static com.github.paulcwarren.ginkgo4j.Ginkgo4jDSL.FIt; import static com.github.paulcwarren.ginkgo4j.Ginkgo4jDSL.It; import static com.github.paulcwarren.ginkgo4j.Ginkgo4jDSL.JustBeforeEach; +import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.nullValue; @@ -25,6 +26,7 @@ public class SpecCollectorTests { private TestWalker walker; private SpecsCollector collector; + private Throwable t; { Describe("Spec Collector", () -> { BeforeEach(() -> { @@ -33,7 +35,11 @@ public class SpecCollectorTests { }); JustBeforeEach(() -> { - walker.walk(collector); + try { + walker.walk(collector); + } catch (Throwable t) { + this.t = t; + } }); It("should collect all specs", () -> { @@ -67,6 +73,56 @@ public class SpecCollectorTests { assertThat(collector.getSpecs().get(0).getId(), is("_EMPTY_._EMPTY_._EMPTY_")); }); }); + + Context("when a describe throws an exception", () -> { + BeforeEach(() -> { + walker = new TestWalker(ExceptionThrowingDescribeTestClass.class); + }); + + It("should throw the exception", () -> { + assertThat(t, instanceOf(NullPointerException.class)); + }); + }); + + Context("when a context throws an exception", () -> { + BeforeEach(() -> { + walker = new TestWalker(ExceptionThrowingContextTestClass.class); + }); + + It("should throw the exception", () -> { + assertThat(t, instanceOf(NullPointerException.class)); + }); + }); + + Context("when a beforeeach throws an exception", () -> { + BeforeEach(() -> { + walker = new TestWalker(ExceptionThrowingBeforeEachTestClass.class); + }); + + It("should throw the exception", () -> { + assertThat(t, is(nullValue())); + }); + }); + + Context("when a justbeforeeach throws an exception", () -> { + BeforeEach(() -> { + walker = new TestWalker(ExceptionThrowingJustBeforeEachTestClass.class); + }); + + It("should not throw the exception", () -> { + assertThat(t, is(nullValue())); + }); + }); + + Context("when an aftereach throws an exception", () -> { + BeforeEach(() -> { + walker = new TestWalker(ExceptionThrowingAfterEachTestClass.class); + }); + + It("should not throw the exception", () -> { + assertThat(t, is(nullValue())); + }); + }); }); } @@ -112,4 +168,75 @@ static class EmptyLabelsTestClass {{ }); }); }} + + static class ExceptionThrowingDescribeTestClass { + private String something; + { + Describe("A describe", () -> { + something.length(); + Context("A context", () -> { + It("A test", () -> { + }); + }); + }); + } + } + + static class ExceptionThrowingContextTestClass { + private String something; + { + Describe("A describe", () -> { + Context("A context", () -> { + something.length(); + It("A test", () -> { + }); + }); + }); + } + } + + static class ExceptionThrowingBeforeEachTestClass { + private String something; + { + Describe("A describe", () -> { + BeforeEach(() -> { + something.length(); + }); + Context("A context", () -> { + It("A test", () -> { + }); + }); + }); + } + } + + static class ExceptionThrowingJustBeforeEachTestClass { + private String something; + { + Describe("A describe", () -> { + JustBeforeEach(() -> { + something.length(); + }); + Context("A context", () -> { + It("A test", () -> { + }); + }); + }); + } + } + + static class ExceptionThrowingAfterEachTestClass { + private String something; + { + Describe("A describe", () -> { + Context("A context", () -> { + It("A test", () -> { + }); + }); + AfterEach(() -> { + something.length(); + }); + }); + } + } }