diff --git a/core/citrus-api/src/main/java/org/citrusframework/TestResultInstanceProvider.java b/core/citrus-api/src/main/java/org/citrusframework/TestResultInstanceProvider.java new file mode 100644 index 0000000000..ed5930531e --- /dev/null +++ b/core/citrus-api/src/main/java/org/citrusframework/TestResultInstanceProvider.java @@ -0,0 +1,13 @@ +package org.citrusframework; + +/** + * Provides methods to create instances of {@link TestResult} for different outcomes of a test case. + * Implementors may decide to create TestResults with specific parameters derived from the {@link TestCase}. + */ +public interface TestResultInstanceProvider { + + TestResult createSuccess(TestCase testCase); + TestResult createFailed(TestCase testCase, Throwable throwable); + TestResult createSkipped(TestCase testCase); + +} diff --git a/core/citrus-base/src/main/java/org/citrusframework/DefaultTestCase.java b/core/citrus-base/src/main/java/org/citrusframework/DefaultTestCase.java index 7ab2da958b..236ad4efde 100644 --- a/core/citrus-base/src/main/java/org/citrusframework/DefaultTestCase.java +++ b/core/citrus-base/src/main/java/org/citrusframework/DefaultTestCase.java @@ -16,6 +16,20 @@ package org.citrusframework; +import static java.lang.String.format; +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; +import static org.citrusframework.TestResult.failed; +import static org.citrusframework.TestResult.skipped; +import static org.citrusframework.TestResult.success; +import static org.citrusframework.util.TestUtils.waitForCompletion; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import org.apache.commons.lang3.time.StopWatch; import org.citrusframework.container.AbstractActionContainer; import org.citrusframework.container.AfterTest; @@ -23,24 +37,10 @@ import org.citrusframework.context.TestContext; import org.citrusframework.exceptions.CitrusRuntimeException; import org.citrusframework.exceptions.TestCaseFailedException; +import org.citrusframework.spi.ReferenceResolver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.time.Duration; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import static java.lang.String.format; -import static java.util.Objects.isNull; -import static java.util.Objects.nonNull; -import static org.citrusframework.TestResult.failed; -import static org.citrusframework.TestResult.skipped; -import static org.citrusframework.TestResult.success; -import static org.citrusframework.util.TestUtils.waitForCompletion; - /** * Default test case implementation holding a list of test actions to execute. Test case also holds variable definitions and * performs the test lifecycle such as start, finish, before and after test. @@ -51,6 +51,8 @@ public class DefaultTestCase extends AbstractActionContainer implements TestCase private static final Logger logger = LoggerFactory.getLogger(DefaultTestCase.class); + private static final TestResultInstanceProvider DEFAULT_TEST_RESULT_INSTANCE_PROVIDER = new DefaultTestResultInstanceProvider(); + /** * Further chain of test actions to be executed in any case (success, error) * Usually used to clean up database in any case of test result @@ -114,16 +116,16 @@ public void doExecute(final TestContext context) { executeAction(actionBuilder.build(), context); } - testResult = success(getName(), testClass.getName()); + testResult = getTestResultInstanceProvider(context).createSuccess(this); } catch (final TestCaseFailedException e) { gracefullyStopTimer(); throw e; } catch (final Exception | AssertionError e) { - testResult = failed(getName(), testClass.getName(), e); + testResult = getTestResultInstanceProvider(context).createFailed(this, e); throw new TestCaseFailedException(e); } } else { - testResult = skipped(getName(), testClass.getName()); + testResult = getTestResultInstanceProvider(context).createSkipped(this); context.getTestListeners().onTestSkipped(this); } } @@ -142,7 +144,7 @@ public void start(final TestContext context) { beforeTest(context); } catch (final Exception | AssertionError e) { - testResult = failed(getName(), testClass.getName(), e); + testResult = getTestResultInstanceProvider(context).createFailed(this, e); throw new TestCaseFailedException(e); } } @@ -193,7 +195,7 @@ public void executeAction(final TestAction action, final TestContext context) { context.getTestActionListeners().onTestActionSkipped(this, action); } } catch (final Exception | AssertionError e) { - testResult = failed(getName(), testClass.getName(), e); + testResult = getTestResultInstanceProvider(context).createFailed(this, e); throw new TestCaseFailedException(e); } finally { setExecutedAction(action); @@ -219,9 +221,9 @@ public void finish(final TestContext context) { if (isNull(testResult)) { if (context.hasExceptions()) { contextException = context.getExceptions().remove(0); - testResult = failed(getName(), testClass.getName(), contextException); + testResult = getTestResultInstanceProvider(context).createFailed(this, contextException); } else { - testResult = success(getName(), testClass.getName()); + testResult = getTestResultInstanceProvider(context).createSuccess(this); } } @@ -238,7 +240,7 @@ public void finish(final TestContext context) { } catch (final TestCaseFailedException e) { throw e; } catch (final Exception | AssertionError e) { - testResult = failed(getName(), testClass.getName(), e); + testResult = getTestResultInstanceProvider(context).createFailed(this, e); throw new TestCaseFailedException(e); } finally { if (testResult != null) { @@ -298,7 +300,7 @@ private void executeFinalActions(TestContext context) { if (testResult.isSuccess() && context.hasExceptions()) { CitrusRuntimeException contextException = context.getExceptions().remove(0); - testResult = failed(getName(), testClass.getName(), contextException); + testResult = getTestResultInstanceProvider(context).createFailed(this, contextException); throw new TestCaseFailedException(contextException); } } @@ -381,6 +383,16 @@ public void setFinalActions(final List finalActions) { this.finalActions = finalActions.stream().map(action -> (TestActionBuilder) () -> action).collect(Collectors.toList()); } + private TestResultInstanceProvider getTestResultInstanceProvider(TestContext context) { + + ReferenceResolver referenceResolver = context.getReferenceResolver(); + if (referenceResolver != null && referenceResolver.isResolvable(TestResultInstanceProvider.class)) { + return referenceResolver.resolve(TestResultInstanceProvider.class); + } + + return DEFAULT_TEST_RESULT_INSTANCE_PROVIDER; + } + @Override public String toString() { final StringBuilder stringBuilder = new StringBuilder(); @@ -527,4 +539,26 @@ public void setTimeout(final long timeout) { public long getTimeout() { return timeout; } + + /** + * Default implementation of {@link TestResultInstanceProvider} that provides simple TestResults + * without any parameters. + */ + private static final class DefaultTestResultInstanceProvider implements TestResultInstanceProvider { + + @Override + public TestResult createSuccess(TestCase testCase) { + return success(testCase.getName(), testCase.getTestClass().getName()); + } + + @Override + public TestResult createFailed(TestCase testCase, Throwable throwable) { + return failed(testCase.getName(), testCase.getTestClass().getName(), throwable); + } + + @Override + public TestResult createSkipped(TestCase testCase) { + return skipped(testCase.getName(), testCase.getTestClass().getName()); + } + } }