### Eclipse Workspace Patch 1.0 #P jbehave-core Index: src/java/org/jbehave/scenario/reporters/WrapThrowableWithStepFailureDecorator.java =================================================================== --- src/java/org/jbehave/scenario/reporters/WrapThrowableWithStepFailureDecorator.java (revision 0) +++ src/java/org/jbehave/scenario/reporters/WrapThrowableWithStepFailureDecorator.java (revision 0) @@ -0,0 +1,65 @@ +package org.jbehave.scenario.reporters; + +import org.jbehave.scenario.definition.Blurb; +import org.jbehave.scenario.errors.ErrorStrategy; +import org.jbehave.scenario.errors.StepFailure; + +/** + *

+ * When a step fails with a {@link Throwable} that throwable is wrapped in a {@link StepFailure} + * to indicate the step during which the failure occurred. If such a failure occurs it will also + * throw the {@link StepFailure} after the story is finished. + *

+ *

+ * Since it rethrows the error before the {@link ErrorStrategy} is called the ErrorStrategy used + * with this {@link ScenarioReporter} is irrelevant. + *

+ * + * @see StepFailure + */ +public class WrapThrowableWithStepFailureDecorator implements ScenarioReporter { + + private final ScenarioReporter delegate; + private StepFailure throwable; + + public WrapThrowableWithStepFailureDecorator(ScenarioReporter delegate) { + this.delegate = delegate; + } + + public void afterScenario() { + delegate.afterScenario(); + } + + public void afterStory() { + delegate.afterStory(); + if (throwable != null) { + throw(throwable); + } + } + + public void beforeScenario(String title) { + delegate.beforeScenario(title); + } + + public void beforeStory(Blurb blurb) { + delegate.beforeStory(blurb); + } + + public void failed(String step, Throwable e) { + throwable = new StepFailure(step, e); + delegate.failed(step, throwable); + } + + public void notPerformed(String step) { + delegate.notPerformed(step); + } + + public void pending(String step) { + delegate.pending(step); + } + + public void successful(String step) { + delegate.successful(step); + } + +} Index: src/java/org/jbehave/scenario/errors/StepFailure.java =================================================================== --- src/java/org/jbehave/scenario/errors/StepFailure.java (revision 0) +++ src/java/org/jbehave/scenario/errors/StepFailure.java (revision 0) @@ -0,0 +1,16 @@ +package org.jbehave.scenario.errors; + +import org.jbehave.scenario.steps.Step; + +/** + * Indicates the {@link Step} during which a failure occurred. + */ +@SuppressWarnings("serial") +public class StepFailure extends AssertionError { + + public StepFailure(String stepAsString, Throwable t) { + super(t.getMessage() + "\nduring step: '" + stepAsString + "'"); + this.initCause(t); + } + +} Index: src/behaviour/org/jbehave/scenario/reporters/WrapThrowableWithStepFailureDecoratorBehaviour.java =================================================================== --- src/behaviour/org/jbehave/scenario/reporters/WrapThrowableWithStepFailureDecoratorBehaviour.java (revision 0) +++ src/behaviour/org/jbehave/scenario/reporters/WrapThrowableWithStepFailureDecoratorBehaviour.java (revision 0) @@ -0,0 +1,105 @@ +package org.jbehave.scenario.reporters; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; +import org.jbehave.scenario.definition.Blurb; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; + +public class WrapThrowableWithStepFailureDecoratorBehaviour { + + private ScenarioReporter delegate; + private WrapThrowableWithStepFailureDecorator decorator; + + @Before + public void createDecorator() { + delegate = mock(ScenarioReporter.class); + decorator = new WrapThrowableWithStepFailureDecorator(delegate); + } + + @Test + public void shouldJustDelegateAllReportingMethodsOtherThanFailure() { + //Given + Blurb blurb = new Blurb("Some blurb"); + + //When + decorator.beforeStory(blurb); + decorator.beforeScenario("My scenario 1"); + decorator.successful("Given step 1.1"); + decorator.pending("When step 1.2"); + decorator.notPerformed("Then step 1.3"); + decorator.afterScenario(); + decorator.afterStory(); + + //Then + InOrder inOrder = inOrder(delegate); + + inOrder.verify(delegate).beforeStory(blurb); + inOrder.verify(delegate).beforeScenario("My scenario 1"); + inOrder.verify(delegate).successful("Given step 1.1"); + inOrder.verify(delegate).pending("When step 1.2"); + inOrder.verify(delegate).notPerformed("Then step 1.3"); + inOrder.verify(delegate).afterScenario(); + inOrder.verify(delegate).afterStory(); + } + + @Test + public void shouldProvideFailureCauseWithMessageDescribingStep() { + //Given + Throwable t = new IllegalArgumentException("World Peace for everyone"); + String stepAsString = "When I have a bad idea"; + + //When + decorator.failed(stepAsString, t); + + //Then + verify(delegate).failed(eq(stepAsString), argThat(hasMessage(t.getMessage() + "\nduring step: '" + stepAsString + "'"))); + } + + @Test + public void shouldRethrowFailureCauseAfterStory() { + //Given + Throwable t = new IllegalArgumentException("World Peace for everyone"); + String stepAsString = "When I have a bad idea"; + decorator.failed(stepAsString, t); + + //When + try { + decorator.afterStory(); + fail("Should have rethrown exception"); + } catch (Throwable rethrown) { + + //Then + assertThat(rethrown, hasMessage(t.getMessage() + "\nduring step: '" + stepAsString + "'")); + } + } + + private Matcher hasMessage(final String string) { + return new TypeSafeMatcher() { + + private Matcher equalTo; + + @Override + public boolean matchesSafely(Throwable t) { + equalTo = equalTo(string); + return equalTo.matches(t.getMessage()); + } + + public void describeTo(Description description) { + description.appendText("Throwable with message: ").appendDescriptionOf(equalTo); + } + }; + } + +} Index: src/behaviour/org/jbehave/scenario/errors/StepFailureBehaviour.java =================================================================== --- src/behaviour/org/jbehave/scenario/errors/StepFailureBehaviour.java (revision 0) +++ src/behaviour/org/jbehave/scenario/errors/StepFailureBehaviour.java (revision 0) @@ -0,0 +1,36 @@ +package org.jbehave.scenario.errors; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.sameInstance; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +public class StepFailureBehaviour { + @Test + public void shouldAppendStepNameToError() { + //Given + Throwable t = new IllegalArgumentException("Can't we all just get along?"); + String stepAsString = "Given something that could never work"; + StepFailure decorator = new StepFailure(stepAsString, t); + + //When + String message = decorator.getMessage(); + + //Then + assertThat(message, equalTo(t.getMessage() + "\nduring step: '" + stepAsString + "'")); + } + + @Test + public void shouldKeepOriginalExceptionAsCause() { + //Given + Throwable t = new IllegalArgumentException("Can't we all just get along?"); + StepFailure decorator = new StepFailure("Given something that could never work", t); + + //When + Throwable cause = decorator.getCause(); + + //Then + assertThat(cause, sameInstance(t)); + } +}