diff --git a/serenity-core/src/main/java/net/thucydides/core/model/TestOutcome.java b/serenity-core/src/main/java/net/thucydides/core/model/TestOutcome.java index d9f67dc040..138fcaf3b3 100644 --- a/serenity-core/src/main/java/net/thucydides/core/model/TestOutcome.java +++ b/serenity-core/src/main/java/net/thucydides/core/model/TestOutcome.java @@ -196,6 +196,10 @@ public class TestOutcome { */ private boolean manual; + /** + * Indicates the test source e.g : junit/jbehave/cucumber + */ + private String testSource; /** * Fields used for serialization @@ -279,6 +283,11 @@ public TestOutcome asManualTest() { return this; } + public TestOutcome withTestSource(String testSource) { + this.testSource = testSource; + return this; + } + public void setEnvironmentVariables(EnvironmentVariables environmentVariables) { this.environmentVariables = environmentVariables; @@ -1403,7 +1412,7 @@ public void lastStepFailedWith(Throwable testFailureCause) { public Set getTags() { if (tags == null) { - tags = getTagsUsingTagProviders(getTagProviderService().getTagProviders()); + tags = getTagsUsingTagProviders(getTagProviderService().getTagProviders(getTestSource())); } return ImmutableSet.copyOf(tags); } @@ -2076,4 +2085,12 @@ public FailureDetails getFailureDetails() { return new FailureDetails(this); } + public String getTestSource() { + return testSource; + } + + public void setTestSource(String testSource) { + this.testSource = testSource; + } + } diff --git a/serenity-core/src/main/java/net/thucydides/core/reports/xml/TestOutcomeConverter.java b/serenity-core/src/main/java/net/thucydides/core/reports/xml/TestOutcomeConverter.java index fa50d61460..4f31a89c8b 100644 --- a/serenity-core/src/main/java/net/thucydides/core/reports/xml/TestOutcomeConverter.java +++ b/serenity-core/src/main/java/net/thucydides/core/reports/xml/TestOutcomeConverter.java @@ -73,6 +73,7 @@ public class TestOutcomeConverter implements Converter { private static final String ROW = "row"; private static final String VALUE = "value"; private static final String MANUAL = "manual"; + private static final String TEST_SOURCE = "test-source"; public static final String NEW_LINE_CHAR = "\n"; public static final String ESCAPE_CHAR_FOR_NEW_LINE = " "; private static final String DEFAULT_ERROR_MESSAGE = "Unspecified failure"; @@ -128,6 +129,9 @@ public void marshal(final Object value, final HierarchicalStreamWriter writer, if (isNotEmpty(testOutcome.getSessionId())) { writer.addAttribute(SESSION_ID, testOutcome.getSessionId()); } + if (isNotEmpty(testOutcome.getTestSource())) { + writer.addAttribute(TEST_SOURCE, testOutcome.getTestSource()); + } addUserStoryTo(writer, testOutcome.getUserStory()); addIssuesTo(writer, testOutcome.getIssues()); addVersionsTo(writer, testOutcome.getVersions()); @@ -422,6 +426,8 @@ public Object unmarshal(final HierarchicalStreamReader reader, } String sessionId = readSessionId(reader); testOutcome.setSessionId(sessionId); + String source = readSource(reader); + testOutcome.setTestSource(source); readChildren(reader, testOutcome); if (savedAnnotatedResult != null) { testOutcome.setAnnotatedResult(savedAnnotatedResult); @@ -665,6 +671,10 @@ private String readSessionId(HierarchicalStreamReader reader) { return reader.getAttribute(SESSION_ID); } + private String readSource(HierarchicalStreamReader reader) { + return reader.getAttribute(TEST_SOURCE); + } + private void readTestGroup(final HierarchicalStreamReader reader, final TestOutcome testOutcome) { String name = reader.getAttribute(NAME_FIELD); String testResultValue = reader.getAttribute(RESULT_FIELD); diff --git a/serenity-core/src/main/java/net/thucydides/core/statistics/service/ClasspathTagProviderService.java b/serenity-core/src/main/java/net/thucydides/core/statistics/service/ClasspathTagProviderService.java index 85ba2dcda7..99118a8ced 100644 --- a/serenity-core/src/main/java/net/thucydides/core/statistics/service/ClasspathTagProviderService.java +++ b/serenity-core/src/main/java/net/thucydides/core/statistics/service/ClasspathTagProviderService.java @@ -20,11 +20,14 @@ public ClasspathTagProviderService() { @Override public List getTagProviders() { + return getTagProviders(null); + } + + @Override + public List getTagProviders(String testSource) { if (tagProviders == null) { List newTagProviders = Lists.newArrayList(); - - Iterable tagProviderServiceLoader = loadTagProvidersFromPath(); - + Iterable tagProviderServiceLoader = loadTagProvidersFromPath(testSource); for (TagProvider tagProvider : tagProviderServiceLoader) { newTagProviders.add(tagProvider); } @@ -33,7 +36,17 @@ public List getTagProviders() { return tagProviders; } - protected Iterable loadTagProvidersFromPath() { - return ServiceLoader.load(TagProvider.class); + protected Iterable loadTagProvidersFromPath(String testSource) { + if (testSource == null) { + return ServiceLoader.load(TagProvider.class); + } else { + Iterable tagProviderStrategies = ServiceLoader.load(TagProviderStrategy.class); + for (TagProviderStrategy strategy : tagProviderStrategies) { + if (strategy.canHandleTestSource(testSource)) { + return strategy.getTagProviders(); + } + } + } + return null; } } diff --git a/serenity-core/src/main/java/net/thucydides/core/statistics/service/JUnitTagProviderStrategy.java b/serenity-core/src/main/java/net/thucydides/core/statistics/service/JUnitTagProviderStrategy.java new file mode 100644 index 0000000000..04054a880f --- /dev/null +++ b/serenity-core/src/main/java/net/thucydides/core/statistics/service/JUnitTagProviderStrategy.java @@ -0,0 +1,20 @@ +package net.thucydides.core.statistics.service; + + +import net.thucydides.core.steps.StepEventBus; + +import java.util.ServiceLoader; + + +public class JUnitTagProviderStrategy implements TagProviderStrategy { + + @Override + public boolean canHandleTestSource(String testType) { + return StepEventBus.TEST_SOURCE_JUNIT.equals(testType); + } + + @Override + public Iterable getTagProviders() { + return ServiceLoader.load(TagProvider.class); + } +} diff --git a/serenity-core/src/main/java/net/thucydides/core/statistics/service/TagProviderService.java b/serenity-core/src/main/java/net/thucydides/core/statistics/service/TagProviderService.java index d4b58405e5..85c9c7a9da 100644 --- a/serenity-core/src/main/java/net/thucydides/core/statistics/service/TagProviderService.java +++ b/serenity-core/src/main/java/net/thucydides/core/statistics/service/TagProviderService.java @@ -3,5 +3,17 @@ import java.util.List; public interface TagProviderService { + + /** + * Return all available tag providers. + * @return + */ List getTagProviders(); + + /** + * Return the TagProviders for a given testSource. + * @param testSource + * @return + */ + List getTagProviders(String testSource); } diff --git a/serenity-core/src/main/java/net/thucydides/core/statistics/service/TagProviderStrategy.java b/serenity-core/src/main/java/net/thucydides/core/statistics/service/TagProviderStrategy.java index 77748527a2..e54fdf48b9 100644 --- a/serenity-core/src/main/java/net/thucydides/core/statistics/service/TagProviderStrategy.java +++ b/serenity-core/src/main/java/net/thucydides/core/statistics/service/TagProviderStrategy.java @@ -1,6 +1,5 @@ package net.thucydides.core.statistics.service; - public interface TagProviderStrategy { public boolean canHandleTestSource(String testSource); diff --git a/serenity-core/src/main/java/net/thucydides/core/steps/BaseStepListener.java b/serenity-core/src/main/java/net/thucydides/core/steps/BaseStepListener.java index 8be5fc4490..d854afa914 100644 --- a/serenity-core/src/main/java/net/thucydides/core/steps/BaseStepListener.java +++ b/serenity-core/src/main/java/net/thucydides/core/steps/BaseStepListener.java @@ -306,6 +306,7 @@ public void testSuiteFinished() { */ public void testStarted(final String testMethod) { TestOutcome newTestOutcome = TestOutcome.forTestInStory(testMethod, testSuite, testedStory); + newTestOutcome.setTestSource(StepEventBus.getEventBus().getTestSource()); testOutcomes.add(newTestOutcome); setAnnotatedResult(testMethod); } diff --git a/serenity-core/src/main/java/net/thucydides/core/steps/StepEventBus.java b/serenity-core/src/main/java/net/thucydides/core/steps/StepEventBus.java index 965bea74cb..4e354524a4 100644 --- a/serenity-core/src/main/java/net/thucydides/core/steps/StepEventBus.java +++ b/serenity-core/src/main/java/net/thucydides/core/steps/StepEventBus.java @@ -33,6 +33,10 @@ public class StepEventBus { private static final String CORE_THUCYDIDES_PACKAGE = "net.thucydides.core"; private static final Logger LOGGER = LoggerFactory.getLogger(StepEventBus.class); + public static final String TEST_SOURCE_JUNIT = "JUnit"; + public static final String TEST_SOURCE_JBEHAVE = "JBehave"; + public static final String TEST_SOURCE_CUCUMBER = "Cucumber"; + /** * The event bus used to inform listening classes about when tests and test steps start and finish. * There is a separate event bus for each thread. @@ -75,6 +79,11 @@ public StepEventBus(EnvironmentVariables environmentVariables) { Darkroom.isOpenForBusiness(); } + /** + * Indicates the test source e.g : junit/jbehave/cucumber + */ + private String testSource; + /** * Register a listener to receive notification at different points during a test's execution. * If you are writing your own listener, you shouldn't need to call this method - just set up your @@ -625,4 +634,13 @@ public void enableSoftAsserts() { public boolean softAssertsActive() { return softAssertsEnabled; } + + public String getTestSource() { + return testSource; + } + + public void setTestSource(String testSource) { + this.testSource = testSource; + } + } diff --git a/serenity-core/src/main/resources/META-INF/services/net.thucydides.core.statistics.service.TagProviderStrategy b/serenity-core/src/main/resources/META-INF/services/net.thucydides.core.statistics.service.TagProviderStrategy new file mode 100644 index 0000000000..dc55a51480 --- /dev/null +++ b/serenity-core/src/main/resources/META-INF/services/net.thucydides.core.statistics.service.TagProviderStrategy @@ -0,0 +1 @@ +net.thucydides.core.statistics.service.JUnitTagProviderStrategy diff --git a/serenity-core/src/test/groovy/net/thucydides/core/reports/json/WhenStoringTestOutcomesAsJSON.groovy b/serenity-core/src/test/groovy/net/thucydides/core/reports/json/WhenStoringTestOutcomesAsJSON.groovy index 29b5aae99b..84c3c049d1 100644 --- a/serenity-core/src/test/groovy/net/thucydides/core/reports/json/WhenStoringTestOutcomesAsJSON.groovy +++ b/serenity-core/src/test/groovy/net/thucydides/core/reports/json/WhenStoringTestOutcomesAsJSON.groovy @@ -7,6 +7,7 @@ import net.thucydides.core.annotations.Issue import net.thucydides.core.annotations.WithTag import net.thucydides.core.annotations.Feature import net.thucydides.core.annotations.Issues +import net.thucydides.core.annotations.Story import net.thucydides.core.digest.Digest import net.thucydides.core.issues.IssueTracking import net.thucydides.core.model.* @@ -16,6 +17,8 @@ import net.thucydides.core.reports.TestOutcomes import net.thucydides.core.reports.integration.TestStepFactory import net.thucydides.core.reports.json.gson.GsonJSONConverter import net.thucydides.core.screenshots.ScreenshotAndHtmlSource +import net.thucydides.core.steps.BaseStepListener +import net.thucydides.core.steps.StepEventBus import net.thucydides.core.util.MockEnvironmentVariables import org.joda.time.DateTime import org.joda.time.LocalDateTime @@ -752,4 +755,19 @@ class WhenStoringTestOutcomesAsJSON extends Specification { then: loadedOutcome.tags } + + def "should include test source in the JSON report"() { + given: + def testOutcome = TestOutcome.forTest("should_do_this", ATestScenarioWithIssues.class) + testOutcome.startTime = FIRST_OF_JANUARY + testOutcome.setTestSource(StepEventBus.TEST_SOURCE_JUNIT) + + when: + def jsonReport = reporter.generateReportFor(testOutcome, allTestOutcomes) + and: + TestOutcome reloadedOutcome = loader.loadReportFrom(jsonReport).get() + then: + reloadedOutcome.getTestSource() == StepEventBus.TEST_SOURCE_JUNIT + } + } \ No newline at end of file diff --git a/serenity-core/src/test/groovy/net/thucydides/core/requirements/WhenUsingACustomRequirementsProvider.groovy b/serenity-core/src/test/groovy/net/thucydides/core/requirements/WhenUsingACustomRequirementsProvider.groovy index 33166e947d..00c8cc17f8 100644 --- a/serenity-core/src/test/groovy/net/thucydides/core/requirements/WhenUsingACustomRequirementsProvider.groovy +++ b/serenity-core/src/test/groovy/net/thucydides/core/requirements/WhenUsingACustomRequirementsProvider.groovy @@ -31,7 +31,7 @@ class WhenUsingACustomRequirementsProvider extends Specification { given: ClasspathTagProviderService tagProviderService = new ClasspathTagProviderService() { @Override - protected Iterable loadTagProvidersFromPath() { + protected Iterable loadTagProvidersFromPath(String testSource) { return [ new FileSystemRequirementsTagProvider(), new CustomRequirementsTagProvider()] } } diff --git a/serenity-core/src/test/groovy/net/thucydides/core/webdriver/integration/WhenDeserializingJSONObjects.groovy b/serenity-core/src/test/groovy/net/thucydides/core/webdriver/integration/WhenDeserializingJSONObjects.groovy index f10245afa0..8999bceebb 100644 --- a/serenity-core/src/test/groovy/net/thucydides/core/webdriver/integration/WhenDeserializingJSONObjects.groovy +++ b/serenity-core/src/test/groovy/net/thucydides/core/webdriver/integration/WhenDeserializingJSONObjects.groovy @@ -8,6 +8,7 @@ import net.thucydides.core.webdriver.javascript.JavascriptExecutorFacade import org.openqa.selenium.WebDriver import org.openqa.selenium.WebDriverException import org.openqa.selenium.chrome.ChromeDriver +import org.openqa.selenium.firefox.FirefoxDriver import sample.deserialization.DeserializationClass import sample.deserialization.DeserializationWithInjection import spock.lang.Ignore diff --git a/serenity-core/src/test/java/net/thucydides/core/reports/integration/WhenGeneratingAnXMLReport.java b/serenity-core/src/test/java/net/thucydides/core/reports/integration/WhenGeneratingAnXMLReport.java index ad67d0ad33..c3e65c97f6 100644 --- a/serenity-core/src/test/java/net/thucydides/core/reports/integration/WhenGeneratingAnXMLReport.java +++ b/serenity-core/src/test/java/net/thucydides/core/reports/integration/WhenGeneratingAnXMLReport.java @@ -10,6 +10,7 @@ import net.thucydides.core.reports.TestOutcomes; import net.thucydides.core.reports.xml.XMLTestOutcomeReporter; import net.thucydides.core.screenshots.ScreenshotAndHtmlSource; +import net.thucydides.core.steps.StepEventBus; import net.thucydides.core.util.ExtendedTemporaryFolder; import org.apache.commons.io.FileUtils; import org.joda.time.DateTime; @@ -952,6 +953,23 @@ public void should_include_exception_stack_dump_for_failing_test() assertThat(generatedReportText, containsString("sample.steps.FailingStep")); } + @Test + public void should_include_test_source() + throws Exception { + TestOutcome testOutcome = TestOutcome.forTest("a_simple_test_case", SomeTestScenario.class); + testOutcome.setTestSource(StepEventBus.TEST_SOURCE_JUNIT); + + TestStep step = TestStepFactory.failingTestStepCalled("step 1"); + step.failedWith(new FailingStep().failsWithMessage("Oh nose!")); + + testOutcome.recordStep(step); + + File xmlReport = reporter.generateReportFor(testOutcome, allTestOutcomes); + String generatedReportText = getStringFrom(xmlReport); + + assertThat(generatedReportText, containsString(StepEventBus.TEST_SOURCE_JUNIT)); + } + private String getStringFrom(File reportFile) throws IOException { return FileUtils.readFileToString(reportFile); } diff --git a/serenity-core/src/test/java/net/thucydides/core/reports/integration/WhenReadingAnXMLReport.java b/serenity-core/src/test/java/net/thucydides/core/reports/integration/WhenReadingAnXMLReport.java index a3e0ef508c..34f58130f4 100644 --- a/serenity-core/src/test/java/net/thucydides/core/reports/integration/WhenReadingAnXMLReport.java +++ b/serenity-core/src/test/java/net/thucydides/core/reports/integration/WhenReadingAnXMLReport.java @@ -392,6 +392,25 @@ public void should_load_the_session_id_from_xml_file() throws Exception { Optional testOutcome = outcomeReporter.loadReportFrom(report); assertThat(testOutcome.get().getSessionId(), is("1234")); } + + @Test + public void should_load_the_test_source_from_xml_file() throws Exception { + String storedReportXML = + "\n" + + " \n" + + " \n" + + " " + + " \n" + + " step 1\n" + + " \n" + + ""; + + File report = temporaryDirectory.newFile("saved-report.xml"); + FileUtils.writeStringToFile(report, storedReportXML); + + Optional testOutcome = outcomeReporter.loadReportFrom(report); + assertThat(testOutcome.get().getTestSource(), is("JUnit")); + } @Test public void should_return_null_feature_if_no_feature_is_present() { diff --git a/serenity-core/src/test/java/net/thucydides/core/statistics/service/WhenFindingTagsForATestOutcome.java b/serenity-core/src/test/java/net/thucydides/core/statistics/service/WhenFindingTagsForATestOutcome.java index 3cde0fb923..d14cb13e71 100644 --- a/serenity-core/src/test/java/net/thucydides/core/statistics/service/WhenFindingTagsForATestOutcome.java +++ b/serenity-core/src/test/java/net/thucydides/core/statistics/service/WhenFindingTagsForATestOutcome.java @@ -51,6 +51,21 @@ public void should_find_the_annotation_tag_provider_by_default() { assertThat(containsAnnotationTagProvider, is(true)); } + @Test + public void junit_tag_provider_strategy_should_find_the_annotation_tag_provider_by_default() { + + TagProviderStrategy tagProviderStrategy = new JUnitTagProviderStrategy(); + Iterable tagProviders = tagProviderStrategy.getTagProviders(); + + boolean containsAnnotationTagProvider = false; + for(TagProvider provider : tagProviders) { + if (provider instanceof AnnotationBasedTagProvider) { + containsAnnotationTagProvider = true; + } + } + assertThat(containsAnnotationTagProvider, is(true)); + } + @Test public void should_also_find_the_file_system_requirements_provider_by_default() { TagProviderService tagProviderService = new ClasspathTagProviderService(); @@ -65,6 +80,20 @@ public void should_also_find_the_file_system_requirements_provider_by_default() assertThat(containsRequirementsProvider, is(true)); } + @Test + public void junit_tag_provider_strategy_should_also_find_the_file_system_requirements_provider_by_default() { + TagProviderStrategy tagProviderStrategy = new JUnitTagProviderStrategy(); + Iterable tagProviders = tagProviderStrategy.getTagProviders(); + + boolean containsRequirementsProvider = false; + for(TagProvider provider : tagProviders) { + if (provider instanceof FileSystemRequirementsTagProvider) { + containsRequirementsProvider = true; + } + } + assertThat(containsRequirementsProvider, is(true)); + } + @Test public void annotation_based_tag_should_return_no_tags_if_the_test_class_is_not_defined() { when(emptyTestOutcome.getTestCase()).thenReturn(null); diff --git a/serenity-junit/src/main/java/net/thucydides/junit/listeners/JUnitStepListener.java b/serenity-junit/src/main/java/net/thucydides/junit/listeners/JUnitStepListener.java index 80b26d4221..59b5184cf8 100644 --- a/serenity-junit/src/main/java/net/thucydides/junit/listeners/JUnitStepListener.java +++ b/serenity-junit/src/main/java/net/thucydides/junit/listeners/JUnitStepListener.java @@ -26,9 +26,9 @@ public class JUnitStepListener extends RunListener { private boolean testStarted; public static JUnitStepListenerBuilder withOutputDirectory(File outputDirectory) { - return new JUnitStepListenerBuilder(outputDirectory); + return new JUnitStepListenerBuilder(outputDirectory); } - + protected JUnitStepListener(Class testClass, BaseStepListener baseStepListener, StepListener... listeners) { testStarted = false; this.baseStepListener = baseStepListener; @@ -71,6 +71,7 @@ public void testStarted(final Description description) { if (testingThisTest(description)) { startTestSuiteForFirstTest(description); StepEventBus.getEventBus().clear(); + StepEventBus.getEventBus().setTestSource(StepEventBus.TEST_SOURCE_JUNIT); StepEventBus.getEventBus().testStarted( Optional.fromNullable(description.getMethodName()).or("Initialisation"), description.getTestClass()); @@ -89,6 +90,7 @@ public void testFinished(final Description description) throws Exception { if (testingThisTest(description)) { updateResultsUsingTestAnnotations(description); StepEventBus.getEventBus().testFinished(); + StepEventBus.getEventBus().setTestSource(null); endTest(); } }