diff --git a/CHANGELOG.md b/CHANGELOG.md index b8be897..82b20a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Changelog ## [Unreleased] +### Added +- Common Stack Trace frames skip in description and logs, by @HardNorth +- `@Issue` and `@Issues` annotations support, by @HardNorth +### Changed +- Client version updated on [5.2.20](https://github.com/reportportal/client-java/releases/tag/5.2.20), by @HardNorth ## [5.3.3] ### Added diff --git a/build.gradle b/build.gradle index 8613d4a..eb9ff15 100644 --- a/build.gradle +++ b/build.gradle @@ -37,7 +37,7 @@ repositories { } dependencies { - api 'com.epam.reportportal:client-java:5.2.14' + api 'com.epam.reportportal:client-java:5.2.20' compileOnly "org.junit.jupiter:junit-jupiter-api:${junit_version}" implementation 'org.slf4j:slf4j-api:2.0.7' diff --git a/src/main/java/com/epam/reportportal/junit5/ReportPortalExtension.java b/src/main/java/com/epam/reportportal/junit5/ReportPortalExtension.java index 4b5a0da..a308e1d 100644 --- a/src/main/java/com/epam/reportportal/junit5/ReportPortalExtension.java +++ b/src/main/java/com/epam/reportportal/junit5/ReportPortalExtension.java @@ -16,9 +16,7 @@ package com.epam.reportportal.junit5; -import com.epam.reportportal.annotations.Description; -import com.epam.reportportal.annotations.ParameterKey; -import com.epam.reportportal.annotations.TestCaseId; +import com.epam.reportportal.annotations.*; import com.epam.reportportal.annotations.attribute.Attributes; import com.epam.reportportal.listeners.ItemStatus; import com.epam.reportportal.listeners.ListenerParameters; @@ -27,16 +25,17 @@ import com.epam.reportportal.service.item.TestCaseIdEntry; import com.epam.reportportal.service.tree.TestItemTree; import com.epam.reportportal.utils.AttributeParser; +import com.epam.reportportal.utils.IssueUtils; import com.epam.reportportal.utils.ParameterUtils; import com.epam.reportportal.utils.TestCaseIdUtils; -import com.epam.reportportal.utils.markdown.MarkdownUtils; +import com.epam.reportportal.utils.formatting.MarkdownUtils; import com.epam.ta.reportportal.ws.model.*; import com.epam.ta.reportportal.ws.model.attribute.ItemAttributesRQ; import com.epam.ta.reportportal.ws.model.launch.StartLaunchRQ; import com.epam.ta.reportportal.ws.model.log.SaveLogRQ; import io.reactivex.Maybe; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.exception.ExceptionUtils; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.*; import org.opentest4j.TestAbortedException; @@ -58,16 +57,16 @@ import static com.epam.reportportal.junit5.utils.ItemTreeUtils.createItemTreeKey; import static com.epam.reportportal.listeners.ItemStatus.*; import static com.epam.reportportal.service.tree.TestItemTree.createTestItemLeaf; +import static com.epam.reportportal.utils.formatting.ExceptionUtils.getStackTrace; import static java.util.Optional.of; import static java.util.Optional.ofNullable; -import static org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace; /* * ReportPortal Extension sends the results of test execution to ReportPortal in RealTime */ public class ReportPortalExtension - implements Extension, BeforeAllCallback, BeforeEachCallback, InvocationInterceptor, AfterTestExecutionCallback, - AfterAllCallback, TestWatcher { + implements Extension, BeforeAllCallback, BeforeEachCallback, InvocationInterceptor, AfterTestExecutionCallback, AfterAllCallback, + TestWatcher { private static final Logger LOGGER = LoggerFactory.getLogger(ReportPortalExtension.class); @@ -91,9 +90,10 @@ public class ReportPortalExtension private static final Map<String, Launch> launchMap = new ConcurrentHashMap<>(); private final Map<ExtensionContext, Maybe<String>> idMapping = new ConcurrentHashMap<>(); private final Map<ExtensionContext, Maybe<String>> testTemplates = new ConcurrentHashMap<>(); + private final Map<ExtensionContext, List<ParameterResource>> testParameters = new ConcurrentHashMap<>(); private final Set<ExtensionContext> failedClassInits = Collections.newSetFromMap(new ConcurrentHashMap<>()); - public static final String DESCRIPTION_TEST_ERROR_FORMAT = "%s\nError: \n%s"; - private final Map<ExtensionContext, Throwable> testThrowable = new ConcurrentHashMap<>(); + public static final String DESCRIPTION_TEST_ERROR_FORMAT = "Error: \n%s"; + @Nonnull protected Optional<Maybe<String>> getItemId(@Nonnull ExtensionContext context) { return ofNullable(idMapping.get(context)); @@ -137,7 +137,7 @@ protected StartLaunchRQ buildStartLaunchRq(ListenerParameters parameters) { rq.setAttributes(attributes); rq.setStartTime(Calendar.getInstance().getTime()); rq.setRerun(parameters.isRerun()); - rq.setRerunOf(StringUtils.isEmpty(parameters.getRerunOf()) ? null : parameters.getRerunOf()); + rq.setRerunOf(StringUtils.isNotBlank(parameters.getRerunOf()) ? parameters.getRerunOf() : null); return rq; } @@ -220,21 +220,15 @@ public void beforeEach(ExtensionContext context) { } @Override - public void interceptBeforeAllMethod(Invocation<Void> invocation, - ReflectiveInvocationContext<Method> invocationContext, ExtensionContext parentContext) throws Throwable { - Maybe<String> id = startBeforeAfter( - invocationContext.getExecutable(), - parentContext, - parentContext, - BEFORE_CLASS - ); + public void interceptBeforeAllMethod(Invocation<Void> invocation, ReflectiveInvocationContext<Method> invocationContext, + ExtensionContext parentContext) throws Throwable { + Maybe<String> id = startBeforeAfter(invocationContext.getExecutable(), parentContext, parentContext, BEFORE_CLASS); finishBeforeAll(invocation, invocationContext, parentContext, id); } @Override - public <T> T interceptTestClassConstructor(Invocation<T> invocation, - ReflectiveInvocationContext<Constructor<T>> invocationContext, - ExtensionContext parentContext) throws Throwable { + public <T> T interceptTestClassConstructor(Invocation<T> invocation, ReflectiveInvocationContext<Constructor<T>> invocationContext, + ExtensionContext parentContext) throws Throwable { try { return invocation.proceed(); } catch (Throwable cause) { @@ -244,28 +238,23 @@ public <T> T interceptTestClassConstructor(Invocation<T> invocation, } @Override - public void interceptBeforeEachMethod(Invocation<Void> invocation, - ReflectiveInvocationContext<Method> invocationContext, ExtensionContext context) throws Throwable { + public void interceptBeforeEachMethod(Invocation<Void> invocation, ReflectiveInvocationContext<Method> invocationContext, + ExtensionContext context) throws Throwable { ExtensionContext parentContext = context.getParent().orElse(context.getRoot()); Maybe<String> id = startBeforeAfter(invocationContext.getExecutable(), parentContext, context, BEFORE_METHOD); finishBeforeEach(invocation, invocationContext, context, id); } @Override - public void interceptAfterAllMethod(Invocation<Void> invocation, - ReflectiveInvocationContext<Method> invocationContext, ExtensionContext parentContext) throws Throwable { - Maybe<String> id = startBeforeAfter( - invocationContext.getExecutable(), - parentContext, - parentContext, - AFTER_CLASS - ); + public void interceptAfterAllMethod(Invocation<Void> invocation, ReflectiveInvocationContext<Method> invocationContext, + ExtensionContext parentContext) throws Throwable { + Maybe<String> id = startBeforeAfter(invocationContext.getExecutable(), parentContext, parentContext, AFTER_CLASS); finishBeforeAfter(invocation, parentContext, id); } @Override - public void interceptAfterEachMethod(Invocation<Void> invocation, - ReflectiveInvocationContext<Method> invocationContext, ExtensionContext context) throws Throwable { + public void interceptAfterEachMethod(Invocation<Void> invocation, ReflectiveInvocationContext<Method> invocationContext, + ExtensionContext context) throws Throwable { ExtensionContext parentContext = context.getParent().orElse(context.getRoot()); Maybe<String> id = startBeforeAfter(invocationContext.getExecutable(), parentContext, context, AFTER_METHOD); finishBeforeAfter(invocation, context, id); @@ -279,8 +268,8 @@ public void interceptTestMethod(Invocation<Void> invocation, ReflectiveInvocatio } @Override - public <T> T interceptTestFactoryMethod(Invocation<T> invocation, - ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable { + public <T> T interceptTestFactoryMethod(Invocation<T> invocation, ReflectiveInvocationContext<Method> invocationContext, + ExtensionContext extensionContext) throws Throwable { startTestItem(extensionContext, invocationContext.getArguments(), SUITE); return invocation.proceed(); } @@ -288,23 +277,34 @@ public <T> T interceptTestFactoryMethod(Invocation<T> invocation, /** * Returns a status of a test based on execution exception * - * @param context JUnit's test context + * @param context JUnit's test context * @param throwable test exception * @return an {@link ItemStatus} */ + @SuppressWarnings("unused") @Nonnull protected ItemStatus getExecutionStatus(@Nonnull final ExtensionContext context, @Nullable final Throwable throwable) { if (throwable == null) { return PASSED; } - testThrowable.put(context, throwable); sendStackTraceToRP(throwable); return IS_ASSUMPTION.test(throwable) ? SKIPPED : FAILED; } + /** + * Returns a status of a test based on whether or not it contains an execution exception + * + * @param context JUnit's test context + * @return an {@link ItemStatus} + */ + @Nonnull + protected ItemStatus getExecutionStatus(@Nonnull final ExtensionContext context) { + return context.getExecutionException().map(t -> getExecutionStatus(context, t)).orElse(PASSED); + } + @Override public void interceptDynamicTest(Invocation<Void> invocation, DynamicTestInvocationContext invocationContext, - ExtensionContext extensionContext) throws Throwable { + ExtensionContext extensionContext) throws Throwable { Optional<ExtensionContext> parent = extensionContext.getParent(); if (parent.map(p -> !idMapping.containsKey(p)).orElse(false)) { List<ExtensionContext> parents = new ArrayList<>(); @@ -330,23 +330,12 @@ public void interceptDynamicTest(Invocation<Void> invocation, DynamicTestInvocat } @Override - public void interceptTestTemplateMethod(Invocation<Void> invocation, - ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable { + public void interceptTestTemplateMethod(Invocation<Void> invocation, ReflectiveInvocationContext<Method> invocationContext, + ExtensionContext extensionContext) throws Throwable { startTestItem(extensionContext, invocationContext.getArguments(), STEP); invocation.proceed(); } - /** - * Returns a status of a test based on whether or not it contains an execution exception - * - * @param context JUnit's test context - * @return an {@link ItemStatus} - */ - @Nonnull - protected ItemStatus getExecutionStatus(@Nonnull final ExtensionContext context) { - return context.getExecutionException().map(t -> getExecutionStatus(context, t)).orElse(PASSED); - } - @Override public void afterTestExecution(ExtensionContext context) { finishTemplates(context); @@ -369,10 +358,9 @@ public void testDisabled(ExtensionContext context, Optional<String> reason) { @Override public void testFailed(ExtensionContext context, Throwable cause) { context.getParent().ifPresent(parent -> { - if(failedClassInits.contains(parent)) { + if (failedClassInits.contains(parent)) { startTestItem(context, STEP); sendStackTraceToRP(cause); - testThrowable.put(context, cause); finishTest(context, FAILED); } }); @@ -420,8 +408,7 @@ protected void finishBeforeEach(Invocation<Void> invocation, ReflectiveInvocatio } } - private void finishBeforeAfter(Invocation<Void> invocation, ExtensionContext context, Maybe<String> id) - throws Throwable { + private void finishBeforeAfter(Invocation<Void> invocation, ExtensionContext context, Maybe<String> id) throws Throwable { try { invocation.proceed(); } catch (Throwable throwable) { @@ -475,30 +462,28 @@ protected void startTestItem(ExtensionContext context, List<Object> arguments, I * @param context JUnit's test context * @param arguments a list of test parameters * @param itemType a type of the item + * @param description a description of the item + * @param startTime a start time of the item */ protected void startTestItem(@Nonnull final ExtensionContext context, @Nonnull final List<Object> arguments, @Nonnull final ItemType itemType, @Nullable final String description, @Nullable final Date startTime) { idMapping.computeIfAbsent(context, c -> { StartTestItemRQ rq = buildStartStepRq(c, arguments, itemType, description, startTime); Launch launch = getLaunch(c); - Maybe<String> itemId = c.getParent() - .flatMap(parent -> Optional.ofNullable(idMapping.get(parent))) - .map(parentTest -> { - Maybe<String> item = launch.startTestItem(parentTest, rq); - if (getReporter().getParameters().isCallbackReportingEnabled()) { - TEST_ITEM_TREE.getTestItems() - .put(createItemTreeKey(rq.getName()), createTestItemLeaf(parentTest, item)); - } - return item; - }) - .orElseGet(() -> { - Maybe<String> item = launch.startTestItem(rq); - if (getReporter().getParameters().isCallbackReportingEnabled()) { - TEST_ITEM_TREE.getTestItems() - .put(createItemTreeKey(rq.getName()), createTestItemLeaf(item)); - } - return item; - }); + + Maybe<String> parentId = c.getParent().flatMap(parent -> Optional.ofNullable(idMapping.get(parent))).orElse(null); + Maybe<String> itemId; + TestItemTree.TestItemLeaf leaf; + if (parentId == null) { + itemId = launch.startTestItem(rq); + leaf = createTestItemLeaf(itemId); + } else { + itemId = launch.startTestItem(parentId, rq); + leaf = createTestItemLeaf(parentId, itemId); + } + if (getReporter().getParameters().isCallbackReportingEnabled()) { + TEST_ITEM_TREE.getTestItems().put(createItemTreeKey(rq.getName()), leaf); + } if (TEMPLATE == itemType) { testTemplates.put(c, itemId); } @@ -515,12 +500,10 @@ protected void startTestItem(@Nonnull final ExtensionContext context, @Nonnull f * @param itemType a method's item type (to display on RP) * @return an ID of the method */ - protected Maybe<String> startBeforeAfter(Method method, ExtensionContext parentContext, ExtensionContext context, - ItemType itemType) { + protected Maybe<String> startBeforeAfter(Method method, ExtensionContext parentContext, ExtensionContext context, ItemType itemType) { Launch launch = getLaunch(context); StartTestItemRQ rq = buildStartConfigurationRq(method, parentContext, context, itemType); - return getItemId(parentContext).map(pid -> launch.startTestItem(pid, rq)) - .orElseGet(() -> launch.startTestItem(rq)); + return getItemId(parentContext).map(pid -> launch.startTestItem(pid, rq)).orElseGet(() -> launch.startTestItem(rq)); } /** @@ -576,8 +559,8 @@ protected void finishTestItem(@Nonnull final ExtensionContext context, @Nonnull Maybe<String> id = idMapping.remove(context); Maybe<OperationCompletionRS> finishResponse = launch.finishTestItem(id, rq); if (getReporter().getParameters().isCallbackReportingEnabled()) { - ofNullable(TEST_ITEM_TREE.getTestItems() - .get(createItemTreeKey(context))).ifPresent(itemLeaf -> itemLeaf.setFinishResponse(finishResponse)); + ofNullable(TEST_ITEM_TREE.getTestItems().get(createItemTreeKey(context))).ifPresent(itemLeaf -> itemLeaf.setFinishResponse( + finishResponse)); } } @@ -605,7 +588,7 @@ private static String getCodeRef(@Nonnull final Method method) { } private static String appendSuffixIfNotEmpty(final String str, @Nonnull final String suffix) { - return str + (suffix.isEmpty() ? "" : "$" + suffix); + return str + (StringUtils.isNotBlank(suffix) ? "$" + suffix : ""); } @Nonnull @@ -639,8 +622,7 @@ protected String getCodeRef(@Nonnull final ExtensionContext context) { * @return an {@link Optional} of a {@link Method} */ protected Optional<Method> getTestMethod(ExtensionContext context) { - return ofNullable(context.getTestMethod() - .orElseGet(() -> context.getParent().flatMap(this::getTestMethod).orElse(null))); + return ofNullable(context.getTestMethod().orElseGet(() -> context.getParent().flatMap(this::getTestMethod).orElse(null))); } /** @@ -661,24 +643,75 @@ protected Optional<Method> getTestMethod(ExtensionContext context) { * @param arguments a list of parameter values * @return a list of parameters */ - protected @Nonnull List<ParameterResource> getParameters(@Nonnull final Method method, - final List<Object> arguments) { + protected @Nonnull List<ParameterResource> getParameters(@Nonnull final Method method, final List<Object> arguments) { return ParameterUtils.getParameters(method, arguments); } + private Optional<Method> getOptionalTestMethod(ExtensionContext context) { + Optional<Method> optionalMethod = context.getTestMethod(); + if (!optionalMethod.isPresent()) { + //if not present means that we are in the dynamic test, in this case we need to move to the parent context + Optional<ExtensionContext> parentContext = context.getParent(); + if (!parentContext.isPresent()) { + return Optional.empty(); + } + return parentContext.get().getTestMethod(); + } + return optionalMethod; + } + + private String getMethodName(String value) { + return value.length() > 1024 ? value.substring(0, 1021) + "..." : value; + } + + /** + * Extension point to customize test step name + * + * @param context JUnit's test context + * @param itemType a test method item type + * @return Test/Step Name being sent to ReportPortal + */ + protected String createStepName(ExtensionContext context, ItemType itemType) { + String name = context.getDisplayName(); + String defaultValue = getMethodName(name); + + if (itemType != STEP) { + return defaultValue; + } + + Optional<Method> optionalMethod = getOptionalTestMethod(context); + if (!optionalMethod.isPresent()) { + return defaultValue; + } + Method method = optionalMethod.get(); + + com.epam.reportportal.annotations.DisplayName displayNameFromMethod = method.getAnnotation(com.epam.reportportal.annotations.DisplayName.class); + if (displayNameFromMethod != null) { + return getMethodName(displayNameFromMethod.value()); + } + + com.epam.reportportal.annotations.DisplayName displayNameFromClass = method.getDeclaringClass() + .getAnnotation(com.epam.reportportal.annotations.DisplayName.class); + if (displayNameFromClass != null) { + return getMethodName(displayNameFromClass.value()); + } + + return defaultValue; + } + /** * Extension point to customize test step creation event/request * * @param context JUnit's test context * @param arguments a test arguments list * @param itemType a test method item type + * @param description a description of the item * @param startTime a start time of the test * @return Request to ReportPortal */ @Nonnull - protected StartTestItemRQ buildStartStepRq(@Nonnull final ExtensionContext context, - @Nonnull final List<Object> arguments, @Nonnull final ItemType itemType, @Nullable final String description, - @Nullable final Date startTime) { + protected StartTestItemRQ buildStartStepRq(@Nonnull final ExtensionContext context, @Nonnull final List<Object> arguments, + @Nonnull final ItemType itemType, @Nullable final String description, @Nullable final Date startTime) { StartTestItemRQ rq = new StartTestItemRQ(); rq.setStartTime(ofNullable(startTime).orElseGet(Calendar.getInstance()::getTime)); rq.setName(createStepName(context, itemType)); @@ -686,10 +719,7 @@ protected StartTestItemRQ buildStartStepRq(@Nonnull final ExtensionContext conte rq.setType(itemType == TEMPLATE ? SUITE.name() : itemType.name()); String codeRef = getCodeRef(context); rq.setCodeRef(codeRef); - rq.setAttributes(context.getTags() - .stream() - .map(it -> new ItemAttributesRQ(null, it)) - .collect(Collectors.toSet())); + rq.setAttributes(context.getTags().stream().map(it -> new ItemAttributesRQ(null, it)).collect(Collectors.toSet())); if (SUITE == itemType) { context.getTestClass().ifPresent(c -> rq.getAttributes().addAll(getAttributes(c))); } @@ -700,6 +730,9 @@ protected StartTestItemRQ buildStartStepRq(@Nonnull final ExtensionContext conte rq.setParameters(getParameters(m, arguments)); return getTestCaseId(m, codeRef, arguments, context.getTestInstance().orElse(null)); }).orElseGet(() -> TestCaseIdUtils.getTestCaseId(codeRef, arguments)); + if (STEP == itemType) { + testParameters.put(context, rq.getParameters()); + } rq.setTestCaseId(ofNullable(caseId).map(TestCaseIdEntry::getId).orElse(null)); return rq; } @@ -758,23 +791,36 @@ protected void createSkippedSteps(ExtensionContext context, Throwable cause) { * @param status a test item execution result * @return Request to ReportPortal */ - @SuppressWarnings("unused") @Nonnull protected FinishTestItemRQ buildFinishTestRq(@Nonnull ExtensionContext context, @Nullable ItemStatus status) { FinishTestItemRQ rq = new FinishTestItemRQ(); - if (status != ItemStatus.PASSED && testThrowable.containsKey(context)) { - ItemType itemType = STEP; - String description = String.format(DESCRIPTION_TEST_ERROR_FORMAT, - createStepDescription(context, itemType), - ExceptionUtils.getStackTrace(testThrowable.get(context))); + ItemStatus myStatus = ofNullable(status).orElseGet(() -> getExecutionStatus(context)); + Optional<Throwable> myException = context.getExecutionException(); + if (myStatus != ItemStatus.PASSED && myException.isPresent()) { + String stepDescription = createStepDescription(context, STEP); + String stackTrace = String.format(DESCRIPTION_TEST_ERROR_FORMAT, getStackTrace(myException.get(), new Throwable())); + String description = StringUtils.isNotBlank(stepDescription) ? + MarkdownUtils.asTwoParts(stepDescription, stackTrace) : + stackTrace; rq.setDescription(description); } + rq.setIssue(getIssue(context)); ofNullable(status).ifPresent(s -> rq.setStatus(s.name())); rq.setEndTime(Calendar.getInstance().getTime()); - testThrowable.remove(context); return rq; } + @Nullable + protected com.epam.ta.reportportal.ws.model.issue.Issue getIssue(@Nonnull ExtensionContext context) { + String stepName = createStepName(context, STEP); + List<ParameterResource> parameters = testParameters.containsKey(context) ? testParameters.remove(context) : Collections.emptyList(); + return getOptionalTestMethod(context).map(m -> ofNullable(m.getAnnotation(Issues.class)).map(i -> IssueUtils.createIssue(i, + stepName, + parameters + )).orElseGet(() -> IssueUtils.createIssue(m.getAnnotation(Issue.class), stepName, parameters))) + .orElse(null); + } + /** * Extension point to customize a test item result on it's finish * @@ -791,44 +837,6 @@ protected FinishTestItemRQ buildFinishTestItemRq(@Nonnull ExtensionContext conte return rq; } - /** - * Extension point to customize test step name - * - * @param context JUnit's test context - * @param itemType a test method item type - * @return Test/Step Name being sent to ReportPortal - */ - protected String createStepName(ExtensionContext context, ItemType itemType) { - String name = context.getDisplayName(); - String defaultValue = getMethodName(name); - - if (itemType != STEP) { - return defaultValue; - } - - Optional<Method> optionalMethod = getOptionalTestMethod(context); - if (!optionalMethod.isPresent()) { - return defaultValue; - } - Method method = optionalMethod.get(); - - com.epam.reportportal.annotations.DisplayName displayNameFromMethod = method.getAnnotation(com.epam.reportportal.annotations.DisplayName.class); - if (displayNameFromMethod != null) { - return getMethodName(displayNameFromMethod.value()); - } - - com.epam.reportportal.annotations.DisplayName displayNameFromClass = method.getDeclaringClass().getAnnotation(com.epam.reportportal.annotations.DisplayName.class); - if (displayNameFromClass != null) { - return getMethodName(displayNameFromClass.value()); - } - - return defaultValue; - } - - private String getMethodName(String value) { - return value.length() > 1024 ? value.substring(0, 1021) + "..." : value; - } - /** * Extension point to customize beforeXXX step name * @@ -861,7 +869,8 @@ protected String createConfigurationName(@Nonnull Class<?> testClass, @Nonnull M /** * Extension point to customize test step description * - * @param context JUnit's test context + * @param context JUnit's test context + * @param itemType a test method item type * @return Test/Step Description being sent to ReportPortal */ @Nonnull @@ -871,37 +880,24 @@ protected String createStepDescription(ExtensionContext context, final ItemType return defaultValue; } Optional<Method> optionalMethod = getOptionalTestMethod(context); - if (!optionalMethod.isPresent()){ + if (!optionalMethod.isPresent()) { return defaultValue; } Method method = optionalMethod.get(); Description descriptionFromMethod = method.getAnnotation(Description.class); - if(descriptionFromMethod != null){ + if (descriptionFromMethod != null) { return descriptionFromMethod.value(); } Description descriptionFromClass = method.getDeclaringClass().getAnnotation(Description.class); - if(descriptionFromClass != null){ + if (descriptionFromClass != null) { return descriptionFromClass.value(); } return defaultValue; } - private Optional<Method> getOptionalTestMethod(ExtensionContext context){ - Optional<Method> optionalMethod = context.getTestMethod(); - if(!optionalMethod.isPresent()){ - //if not present means that we are in the dynamic test, in this case we need to move to the parent context - Optional<ExtensionContext> parentContext = context.getParent(); - if(!parentContext.isPresent()){ - return Optional.empty(); - } - return parentContext.get().getTestMethod(); - } - return optionalMethod; - } - /** * Extension point to customize beforeXXX step description * @@ -922,8 +918,8 @@ protected String createConfigurationDescription(Class<?> testClass, Method metho * @param throwable An exception which caused the skip * @param eventTime <code>@BeforeEach</code> start time */ - protected void reportSkippedStep(ReflectiveInvocationContext<Method> invocationContext, ExtensionContext context, - Throwable throwable, Date eventTime) { + protected void reportSkippedStep(ReflectiveInvocationContext<Method> invocationContext, ExtensionContext context, Throwable throwable, + Date eventTime) { Date skipStartTime = Calendar.getInstance().getTime(); if (skipStartTime.after(eventTime)) { // to fix item ordering when @AfterEach starts in the same millisecond as skipped test @@ -945,8 +941,8 @@ protected void reportSkippedStep(ReflectiveInvocationContext<Method> invocationC * @param eventTime <code>@BeforeAll</code> start time */ @SuppressWarnings("unused") - protected void reportSkippedClassTests(ReflectiveInvocationContext<Method> invocationContext, - ExtensionContext context, Date eventTime) { + protected void reportSkippedClassTests(ReflectiveInvocationContext<Method> invocationContext, ExtensionContext context, + Date eventTime) { } /** @@ -961,7 +957,7 @@ protected void sendStackTraceToRP(final Throwable cause) { rq.setLevel("ERROR"); rq.setLogTime(Calendar.getInstance().getTime()); if (cause != null) { - rq.setMessage(getStackTrace(cause)); + rq.setMessage(getStackTrace(cause, new Throwable())); } else { rq.setMessage("Test has failed without exception"); } diff --git a/src/test/java/com/epam/reportportal/junit5/BasicTest.java b/src/test/java/com/epam/reportportal/junit5/BasicTest.java index b201217..a803465 100644 --- a/src/test/java/com/epam/reportportal/junit5/BasicTest.java +++ b/src/test/java/com/epam/reportportal/junit5/BasicTest.java @@ -16,7 +16,7 @@ package com.epam.reportportal.junit5; -import com.epam.reportportal.junit5.features.basic.TestFailure; +import com.epam.reportportal.junit5.features.TestFailure; import com.epam.reportportal.junit5.util.TestUtils; import com.epam.reportportal.listeners.ItemStatus; import com.epam.reportportal.service.Launch; diff --git a/src/test/java/com/epam/reportportal/junit5/ErrorLastLogTest.java b/src/test/java/com/epam/reportportal/junit5/ErrorLastLogTest.java index 734eb42..1f3fb19 100644 --- a/src/test/java/com/epam/reportportal/junit5/ErrorLastLogTest.java +++ b/src/test/java/com/epam/reportportal/junit5/ErrorLastLogTest.java @@ -8,6 +8,7 @@ import com.epam.reportportal.service.Launch; import com.epam.reportportal.service.ReportPortal; import com.epam.reportportal.service.ReportPortalClient; +import com.epam.reportportal.utils.formatting.MarkdownUtils; import com.epam.ta.reportportal.ws.model.FinishTestItemRQ; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; @@ -30,122 +31,132 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class ErrorLastLogTest { - private final String testErrorMessagePattern = "%s\nError: \n%s"; - private final String assertErrorMessage = "org.opentest4j.AssertionFailedError: expected: <0> but was: <1>"; - private final String exceptionStepErrorMessage = "java.util.NoSuchElementException: Error message"; - private final String testExceptionMessage = "java.lang.RuntimeException: Critical error"; - private final String assertDescriptionMessage = "0 and 1 is not equal"; - private final String stepDescriptionMessage = "Login issue"; - private final String failedStatus = "FAILED"; - private final String passedStatus = "PASSED"; - static final String testClassUuid = namedId("class"); - static final String testMethodUuid = namedId("test"); - public static class ErrorDescriptionTestExtension extends ReportPortalExtension { + private static final String TEST_ERROR_MESSAGE_PATTERN = "Error: \n%s"; + private static final String ASSERT_ERROR_MESSAGE = "org.opentest4j.AssertionFailedError: expected: <0> but was: <1>"; + private static final String EXCEPTION_STEP_ERROR_MESSAGE = "java.util.NoSuchElementException: Error message"; + private static final String TEST_EXCEPTION_MESSAGE = "java.lang.RuntimeException: Critical error"; + private static final String ASSERT_DESCRIPTION_MESSAGE = "0 and 1 is not equal"; + private static final String STEP_DESCRIPTION_MESSAGE = "Login issue"; + private static final String FAILED_STATUS = "FAILED"; + private static final String PASSED_STATUS = "PASSED"; + private static final String TEST_CLASS_UUID = namedId("class"); + private static final String TEST_METHOD_UUID = namedId("test"); - static final ThreadLocal<ReportPortalClient> client = new ThreadLocal<>(); - static final ThreadLocal<Launch> launch = new ThreadLocal<>(); - public static void init() { - client.set(mock(ReportPortalClient.class)); - TestUtils.mockLaunch(client.get(), "launchUuid", testClassUuid, testMethodUuid); - TestUtils.mockLogging(client.get()); - ReportPortal reportPortal = ReportPortal.create(client.get(), TestUtils.standardParameters()); - launch.set(reportPortal.newLaunch(TestUtils.launchRQ(reportPortal.getParameters()))); + public static class ErrorDescriptionTestExtension extends ReportPortalExtension { - } + static final ThreadLocal<ReportPortalClient> client = new ThreadLocal<>(); + static final ThreadLocal<Launch> launch = new ThreadLocal<>(); - @Override - protected Launch getLaunch(ExtensionContext context) { - return launch.get(); - } + public static void init() { + client.set(mock(ReportPortalClient.class)); + TestUtils.mockLaunch(client.get(), "launchUuid", TEST_CLASS_UUID, TEST_METHOD_UUID); + TestUtils.mockLogging(client.get()); + ReportPortal reportPortal = ReportPortal.create(client.get(), TestUtils.standardParameters()); + launch.set(reportPortal.newLaunch(TestUtils.launchRQ(reportPortal.getParameters()))); - } + } - public static class Listener implements TestExecutionListener { - public Deque<TestExecutionResult> results = new ConcurrentLinkedDeque<>(); + @Override + protected Launch getLaunch(ExtensionContext context) { + return launch.get(); + } - @Override - public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) { - if (testIdentifier.isTest()) { - results.add(testExecutionResult); - } - } - } + } - @Test - public void verify_last_error_log_exception() { - ErrorDescriptionTestExtension.init(); - Listener listener = new Listener(); - TestUtils.runClasses(listener, ErrorLastLogFeatureWithExceptionTest.class); + public static class Listener implements TestExecutionListener { + public Deque<TestExecutionResult> results = new ConcurrentLinkedDeque<>(); - ReportPortalClient client = ErrorDescriptionTestExtension.client.get(); + @Override + public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) { + if (testIdentifier.isTest()) { + results.add(testExecutionResult); + } + } + } - ArgumentCaptor<FinishTestItemRQ> finishTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); - verify(client, timeout(1000).atLeastOnce()).finishTestItem(same(testMethodUuid), finishTestCaptor.capture()); + @Test + public void verify_last_error_log_exception() { + ErrorDescriptionTestExtension.init(); + Listener listener = new Listener(); + TestUtils.runClasses(listener, ErrorLastLogFeatureWithExceptionTest.class); + ReportPortalClient client = ErrorDescriptionTestExtension.client.get(); - List<FinishTestItemRQ> finishTests = finishTestCaptor.getAllValues(); + ArgumentCaptor<FinishTestItemRQ> finishTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(client, timeout(1000).atLeastOnce()).finishTestItem(same(TEST_METHOD_UUID), finishTestCaptor.capture()); - FinishTestItemRQ testCaseWithException = finishTests.get(0); + List<FinishTestItemRQ> finishTests = finishTestCaptor.getAllValues(); - assertThat(testCaseWithException.getDescription(), startsWith(String.format(testErrorMessagePattern, "", testExceptionMessage))); - assertThat(testCaseWithException.getStatus(), equalTo(failedStatus)); + FinishTestItemRQ testCaseWithException = finishTests.get(0); - } + assertThat(testCaseWithException.getDescription(), startsWith(String.format(TEST_ERROR_MESSAGE_PATTERN, TEST_EXCEPTION_MESSAGE))); + assertThat(testCaseWithException.getStatus(), equalTo(FAILED_STATUS)); - @Test - public void verify_last_error_log_step() { - ErrorDescriptionTestExtension.init(); - Listener listener = new Listener(); - TestUtils.runClasses(listener, ErrorLastLogFeatureWithStepTest.class); + } - ReportPortalClient client = ErrorDescriptionTestExtension.client.get(); + @Test + public void verify_last_error_log_step() { + ErrorDescriptionTestExtension.init(); + Listener listener = new Listener(); + TestUtils.runClasses(listener, ErrorLastLogFeatureWithStepTest.class); - ArgumentCaptor<FinishTestItemRQ> finishTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); - verify(client, timeout(1000).atLeastOnce()).finishTestItem(same(testMethodUuid), finishTestCaptor.capture()); + ReportPortalClient client = ErrorDescriptionTestExtension.client.get(); - List<FinishTestItemRQ> finishTests = finishTestCaptor.getAllValues(); + ArgumentCaptor<FinishTestItemRQ> finishTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(client, timeout(1000).atLeastOnce()).finishTestItem(same(TEST_METHOD_UUID), finishTestCaptor.capture()); - FinishTestItemRQ testCaseWithDescriptionAndStepError = finishTests.get(0); + List<FinishTestItemRQ> finishTests = finishTestCaptor.getAllValues(); - assertThat(testCaseWithDescriptionAndStepError.getDescription(), startsWith(String.format(testErrorMessagePattern, stepDescriptionMessage, exceptionStepErrorMessage))); - assertThat(testCaseWithDescriptionAndStepError.getStatus(), equalTo(failedStatus)); - } + FinishTestItemRQ testCaseWithDescriptionAndStepError = finishTests.get(0); - @Test - public void verify_last_error_log_assertion_error() { - ErrorDescriptionTestExtension.init(); - Listener listener = new Listener(); - TestUtils.runClasses(listener, ErrorLastLogFeatureWithAssertionErrorTest.class); + assertThat(testCaseWithDescriptionAndStepError.getDescription(), + startsWith(MarkdownUtils.asTwoParts(STEP_DESCRIPTION_MESSAGE, + String.format(TEST_ERROR_MESSAGE_PATTERN, EXCEPTION_STEP_ERROR_MESSAGE) + )) + ); + assertThat(testCaseWithDescriptionAndStepError.getStatus(), equalTo(FAILED_STATUS)); + } - ReportPortalClient client = ErrorDescriptionTestExtension.client.get(); + @Test + public void verify_last_error_log_assertion_error() { + ErrorDescriptionTestExtension.init(); + Listener listener = new Listener(); + TestUtils.runClasses(listener, ErrorLastLogFeatureWithAssertionErrorTest.class); - ArgumentCaptor<FinishTestItemRQ> finishTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); - verify(client, timeout(1000).atLeastOnce()).finishTestItem(same(testMethodUuid), finishTestCaptor.capture()); + ReportPortalClient client = ErrorDescriptionTestExtension.client.get(); - List<FinishTestItemRQ> finishTests = finishTestCaptor.getAllValues(); + ArgumentCaptor<FinishTestItemRQ> finishTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(client, timeout(1000).atLeastOnce()).finishTestItem(same(TEST_METHOD_UUID), finishTestCaptor.capture()); - FinishTestItemRQ testCaseAssertException = finishTests.get(0); + List<FinishTestItemRQ> finishTests = finishTestCaptor.getAllValues(); - assertThat(testCaseAssertException.getDescription(), startsWith(String.format(testErrorMessagePattern, assertDescriptionMessage, assertErrorMessage))); - assertThat(testCaseAssertException.getStatus(), equalTo(failedStatus)); - } + FinishTestItemRQ testCaseAssertException = finishTests.get(0); - @Test - public void verify_last_error_log_assertion_passed() { - ErrorDescriptionTestExtension.init(); - Listener listener = new Listener(); - TestUtils.runClasses(listener, ErrorLastLogFeatureWithAssertionPassedTest.class); + assertThat(testCaseAssertException.getDescription(), + startsWith(MarkdownUtils.asTwoParts( + ASSERT_DESCRIPTION_MESSAGE, + String.format(TEST_ERROR_MESSAGE_PATTERN, ASSERT_ERROR_MESSAGE) + )) + ); + assertThat(testCaseAssertException.getStatus(), equalTo(FAILED_STATUS)); + } - ReportPortalClient client = ErrorDescriptionTestExtension.client.get(); + @Test + public void verify_last_error_log_assertion_passed() { + ErrorDescriptionTestExtension.init(); + Listener listener = new Listener(); + TestUtils.runClasses(listener, ErrorLastLogFeatureWithAssertionPassedTest.class); - ArgumentCaptor<FinishTestItemRQ> finishTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); - verify(client, timeout(1000).atLeastOnce()).finishTestItem(same(testMethodUuid), finishTestCaptor.capture()); + ReportPortalClient client = ErrorDescriptionTestExtension.client.get(); - List<FinishTestItemRQ> finishTests = finishTestCaptor.getAllValues(); + ArgumentCaptor<FinishTestItemRQ> finishTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(client, timeout(1000).atLeastOnce()).finishTestItem(same(TEST_METHOD_UUID), finishTestCaptor.capture()); - FinishTestItemRQ testCaseWithDescriptionAndPassed = finishTests.get(0); + List<FinishTestItemRQ> finishTests = finishTestCaptor.getAllValues(); - assertThat(testCaseWithDescriptionAndPassed.getStatus(), equalTo(passedStatus)); + FinishTestItemRQ testCaseWithDescriptionAndPassed = finishTests.get(0); - } + assertThat(testCaseWithDescriptionAndPassed.getStatus(), equalTo(PASSED_STATUS)); + + } } diff --git a/src/test/java/com/epam/reportportal/junit5/IssueReportingTest.java b/src/test/java/com/epam/reportportal/junit5/IssueReportingTest.java new file mode 100644 index 0000000..7547417 --- /dev/null +++ b/src/test/java/com/epam/reportportal/junit5/IssueReportingTest.java @@ -0,0 +1,244 @@ +/* + * Copyright 2024 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.epam.reportportal.junit5; + +import com.epam.reportportal.junit5.features.issue.*; +import com.epam.reportportal.junit5.util.TestUtils; +import com.epam.reportportal.listeners.ItemStatus; +import com.epam.reportportal.service.Launch; +import com.epam.reportportal.util.test.CommonUtils; +import com.epam.ta.reportportal.ws.model.FinishTestItemRQ; +import com.epam.ta.reportportal.ws.model.OperationCompletionRS; +import com.epam.ta.reportportal.ws.model.issue.Issue; +import io.reactivex.Maybe; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.mockito.ArgumentCaptor; +import org.mockito.stubbing.Answer; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.Queue; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@TestInstance(TestInstance.Lifecycle.PER_METHOD) +public class IssueReportingTest { + public static class TestExtension extends ReportPortalExtension { + static Launch LAUNCH; + + @Override + protected Launch getLaunch(ExtensionContext context) { + return LAUNCH; + } + } + + private final String suiteId = CommonUtils.namedId("suite_"); + private final Maybe<String> suiteMaybe = Maybe.just(suiteId); + private final String stepOneId = CommonUtils.namedId("step_"); + private final Maybe<String> stepOneMaybe = Maybe.just(stepOneId); + private final String stepTwoId = CommonUtils.namedId("step_"); + private final Maybe<String> stepTwoMaybe = Maybe.just(stepTwoId); + private final String stepThreeId = CommonUtils.namedId("step_"); + private final Maybe<String> stepThreeMaybe = Maybe.just(stepThreeId); + private final Queue<Maybe<String>> stepIds = new LinkedList<>(Arrays.asList(stepOneMaybe, stepTwoMaybe, stepThreeMaybe)); + + @BeforeAll + public static void setupProperty() { + System.setProperty("reportDisabledTests", Boolean.TRUE.toString()); + } + + @BeforeEach + public void setupMock() { + Launch launch = mock(Launch.class); + IssueReportingTest.TestExtension.LAUNCH = launch; + when(launch.startTestItem(any())).thenAnswer((Answer<Maybe<String>>) invocation -> suiteMaybe); + when(launch.startTestItem(any(), any())).thenAnswer((Answer<Maybe<String>>) invocation -> CommonUtils.createMaybeUuid()); + when(launch.startTestItem(same(suiteMaybe), any())).thenAnswer((Answer<Maybe<String>>) invocation -> stepIds.poll()); + when(launch.startTestItem(same(stepOneMaybe), any())).thenAnswer((Answer<Maybe<String>>) invocation -> stepIds.poll()); + when(launch.finishTestItem(any(), + any() + )).thenAnswer((Answer<Maybe<OperationCompletionRS>>) invocation -> Maybe.just(new OperationCompletionRS("OK"))); + } + + @Test + public void verify_simple_test_failure() { + TestUtils.runClasses(SimpleIssueTest.class); + + Launch launch = IssueReportingTest.TestExtension.LAUNCH; + ArgumentCaptor<FinishTestItemRQ> testCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(launch).finishTestItem(same(stepOneMaybe), testCaptor.capture()); + + FinishTestItemRQ finishTestItemRQ = testCaptor.getValue(); + assertThat(finishTestItemRQ.getStatus(), equalTo(ItemStatus.FAILED.name())); + assertThat(finishTestItemRQ.getIssue(), notNullValue()); + Issue issue = finishTestItemRQ.getIssue(); + assertThat(issue.getIssueType(), equalTo("pb001")); + assertThat(issue.getComment(), equalTo(SimpleIssueTest.FAILURE_MESSAGE)); + } + + @Test + public void verify_test_failure_with_two_issues() { + TestUtils.runClasses(SimpleTwoIssuesTest.class); + + Launch launch = IssueReportingTest.TestExtension.LAUNCH; + ArgumentCaptor<FinishTestItemRQ> testCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(launch).finishTestItem(same(stepOneMaybe), testCaptor.capture()); + + FinishTestItemRQ finishTestItemRQ = testCaptor.getValue(); + assertThat(finishTestItemRQ.getStatus(), equalTo(ItemStatus.FAILED.name())); + assertThat(finishTestItemRQ.getIssue(), notNullValue()); + Issue issue = finishTestItemRQ.getIssue(); + assertThat(issue.getIssueType(), equalTo("ab001")); + assertThat(issue.getComment(), equalTo(SimpleTwoIssuesTest.FAILURE_MESSAGE)); + } + + @Test + public void verify_parameterized_test_failure_with_one_issue() { + TestUtils.runClasses(ParameterizedWithOneIssueTest.class); + + Launch launch = IssueReportingTest.TestExtension.LAUNCH; + ArgumentCaptor<FinishTestItemRQ> firstTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(launch).finishTestItem(same(stepTwoMaybe), firstTestCaptor.capture()); + ArgumentCaptor<FinishTestItemRQ> secondTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(launch).finishTestItem(same(stepThreeMaybe), secondTestCaptor.capture()); + + FinishTestItemRQ finishTestItemRQ = firstTestCaptor.getValue(); + assertThat(finishTestItemRQ.getStatus(), equalTo(ItemStatus.FAILED.name())); + assertThat(finishTestItemRQ.getIssue(), nullValue()); + + finishTestItemRQ = secondTestCaptor.getValue(); + assertThat(finishTestItemRQ.getStatus(), equalTo(ItemStatus.FAILED.name())); + assertThat(finishTestItemRQ.getIssue(), notNullValue()); + Issue issue = finishTestItemRQ.getIssue(); + assertThat(issue.getIssueType(), equalTo("ab001")); + assertThat(issue.getComment(), equalTo(ParameterizedWithOneIssueTest.ISSUE_MESSAGE)); + } + + @Test + public void verify_parameterized_test_failure_with_two_issues() { + TestUtils.runClasses(ParameterizedWithTwoIssueTest.class); + + Launch launch = IssueReportingTest.TestExtension.LAUNCH; + ArgumentCaptor<FinishTestItemRQ> firstTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(launch).finishTestItem(same(stepTwoMaybe), firstTestCaptor.capture()); + ArgumentCaptor<FinishTestItemRQ> secondTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(launch).finishTestItem(same(stepThreeMaybe), secondTestCaptor.capture()); + + FinishTestItemRQ finishTestItemRQ = firstTestCaptor.getValue(); + assertThat(finishTestItemRQ.getStatus(), equalTo(ItemStatus.FAILED.name())); + assertThat(finishTestItemRQ.getIssue(), notNullValue()); + Issue issue = finishTestItemRQ.getIssue(); + assertThat(issue.getIssueType(), equalTo("ab001")); + assertThat(issue.getComment(), equalTo(ParameterizedWithTwoIssueTest.ISSUE_MESSAGE)); + + finishTestItemRQ = secondTestCaptor.getValue(); + assertThat(finishTestItemRQ.getStatus(), equalTo(ItemStatus.FAILED.name())); + assertThat(finishTestItemRQ.getIssue(), notNullValue()); + issue = finishTestItemRQ.getIssue(); + assertThat(issue.getIssueType(), equalTo("pb001")); + assertThat(issue.getComment(), equalTo(ParameterizedWithTwoIssueTest.ISSUE_MESSAGE)); + } + + @Test + public void verify_dynamic_test_failure() { + TestUtils.runClasses(DynamicIssueTest.class); + + Launch launch = IssueReportingTest.TestExtension.LAUNCH; + ArgumentCaptor<FinishTestItemRQ> testCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(launch).finishTestItem(same(stepTwoMaybe), testCaptor.capture()); + + FinishTestItemRQ finishTestItemRQ = testCaptor.getValue(); + assertThat(finishTestItemRQ.getStatus(), equalTo(ItemStatus.FAILED.name())); + assertThat(finishTestItemRQ.getIssue(), notNullValue()); + Issue issue = finishTestItemRQ.getIssue(); + assertThat(issue.getIssueType(), equalTo("ab001")); + assertThat(issue.getComment(), equalTo(DynamicIssueTest.FAILURE_MESSAGE)); + } + + @Test + public void verify_two_dynamic_test_failures() { + TestUtils.runClasses(TwoDynamicIssueTest.class); + + Launch launch = IssueReportingTest.TestExtension.LAUNCH; + ArgumentCaptor<FinishTestItemRQ> firstTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(launch).finishTestItem(same(stepTwoMaybe), firstTestCaptor.capture()); + ArgumentCaptor<FinishTestItemRQ> secondTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(launch).finishTestItem(same(stepThreeMaybe), secondTestCaptor.capture()); + + FinishTestItemRQ finishTestItemRQ = firstTestCaptor.getValue(); + assertThat(finishTestItemRQ.getStatus(), equalTo(ItemStatus.FAILED.name())); + assertThat(finishTestItemRQ.getIssue(), notNullValue()); + Issue issue = finishTestItemRQ.getIssue(); + assertThat(issue.getIssueType(), equalTo("ab001")); + assertThat(issue.getComment(), equalTo(TwoDynamicIssueTest.FAILURE_MESSAGE)); + + finishTestItemRQ = secondTestCaptor.getValue(); + assertThat(finishTestItemRQ.getStatus(), equalTo(ItemStatus.FAILED.name())); + assertThat(finishTestItemRQ.getIssue(), notNullValue()); + issue = finishTestItemRQ.getIssue(); + assertThat(issue.getIssueType(), equalTo("ab001")); + assertThat(issue.getComment(), equalTo(TwoDynamicIssueTest.FAILURE_MESSAGE)); + } + + @Test + public void verify_two_dynamic_test_failures_two_issues() { + TestUtils.runClasses(TwoDynamicTwoIssueTest.class); + + Launch launch = IssueReportingTest.TestExtension.LAUNCH; + ArgumentCaptor<FinishTestItemRQ> firstTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(launch).finishTestItem(same(stepTwoMaybe), firstTestCaptor.capture()); + ArgumentCaptor<FinishTestItemRQ> secondTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(launch).finishTestItem(same(stepThreeMaybe), secondTestCaptor.capture()); + + FinishTestItemRQ finishTestItemRQ = firstTestCaptor.getValue(); + assertThat(finishTestItemRQ.getStatus(), equalTo(ItemStatus.FAILED.name())); + assertThat(finishTestItemRQ.getIssue(), notNullValue()); + Issue issue = finishTestItemRQ.getIssue(); + assertThat(issue.getIssueType(), equalTo("ab001")); + assertThat(issue.getComment(), equalTo(TwoDynamicTwoIssueTest.FAILURE_MESSAGE)); + + finishTestItemRQ = secondTestCaptor.getValue(); + assertThat(finishTestItemRQ.getStatus(), equalTo(ItemStatus.FAILED.name())); + assertThat(finishTestItemRQ.getIssue(), notNullValue()); + issue = finishTestItemRQ.getIssue(); + assertThat(issue.getIssueType(), equalTo("pb001")); + assertThat(issue.getComment(), equalTo(TwoDynamicTwoIssueTest.FAILURE_MESSAGE)); + } + + @Test + public void verify_simple_test_skip() { + TestUtils.runClasses(SimpleSkippedIssueTest.class); + + Launch launch = IssueReportingTest.TestExtension.LAUNCH; + ArgumentCaptor<FinishTestItemRQ> testCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class); + verify(launch).finishTestItem(same(stepOneMaybe), testCaptor.capture()); + + FinishTestItemRQ finishTestItemRQ = testCaptor.getValue(); + assertThat(finishTestItemRQ.getStatus(), equalTo(ItemStatus.SKIPPED.name())); + assertThat(finishTestItemRQ.getIssue(), notNullValue()); + Issue issue = finishTestItemRQ.getIssue(); + assertThat(issue.getIssueType(), equalTo("pb001")); + assertThat(issue.getComment(), equalTo(SimpleSkippedIssueTest.FAILURE_MESSAGE)); + } +} diff --git a/src/test/java/com/epam/reportportal/junit5/features/basic/TestFailure.java b/src/test/java/com/epam/reportportal/junit5/features/TestFailure.java similarity index 87% rename from src/test/java/com/epam/reportportal/junit5/features/basic/TestFailure.java rename to src/test/java/com/epam/reportportal/junit5/features/TestFailure.java index 159ba85..31159aa 100644 --- a/src/test/java/com/epam/reportportal/junit5/features/basic/TestFailure.java +++ b/src/test/java/com/epam/reportportal/junit5/features/TestFailure.java @@ -1,11 +1,11 @@ /* - * Copyright 2022 EPAM Systems + * Copyright 2024 EPAM Systems * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.epam.reportportal.junit5.features.basic; +package com.epam.reportportal.junit5.features; import com.epam.reportportal.junit5.BasicTest; import org.junit.jupiter.api.Assertions; diff --git a/src/test/java/com/epam/reportportal/junit5/features/issue/DynamicIssueTest.java b/src/test/java/com/epam/reportportal/junit5/features/issue/DynamicIssueTest.java new file mode 100644 index 0000000..875cc43 --- /dev/null +++ b/src/test/java/com/epam/reportportal/junit5/features/issue/DynamicIssueTest.java @@ -0,0 +1,24 @@ +package com.epam.reportportal.junit5.features.issue; + +import com.epam.reportportal.annotations.Issue; +import com.epam.reportportal.junit5.IssueReportingTest; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.DynamicTest.dynamicTest; + +@ExtendWith(IssueReportingTest.TestExtension.class) +public class DynamicIssueTest { + public static final String FAILURE_MESSAGE = "This test is expected to fail"; + + @TestFactory + @Issue(value = "ab001", comment = FAILURE_MESSAGE) + Stream<DynamicTest> testForTestFactory() { + return Stream.of(dynamicTest("My dynamic test", () -> { + throw new IllegalStateException(FAILURE_MESSAGE); + })); + } +} diff --git a/src/test/java/com/epam/reportportal/junit5/features/issue/ParameterizedWithOneIssueTest.java b/src/test/java/com/epam/reportportal/junit5/features/issue/ParameterizedWithOneIssueTest.java new file mode 100644 index 0000000..1010f5c --- /dev/null +++ b/src/test/java/com/epam/reportportal/junit5/features/issue/ParameterizedWithOneIssueTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2024 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.epam.reportportal.junit5.features.issue; + +import com.epam.reportportal.annotations.Issue; +import com.epam.reportportal.annotations.TestFilter; +import com.epam.reportportal.annotations.TestParamFilter; +import com.epam.reportportal.junit5.IssueReportingTest; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +@ExtendWith(IssueReportingTest.TestExtension.class) +public class ParameterizedWithOneIssueTest { + + public static final String FAILURE_MESSAGE = "This parameterized test is expected to fail: "; + public static final String ISSUE_MESSAGE = "This test is expected to fail"; + + @ParameterizedTest + @ValueSource(booleans = { true, false }) + @Issue(value = "ab001", comment = ISSUE_MESSAGE, filter = @TestFilter(param = { @TestParamFilter(valueStartsWith = "false") })) + public void failureTest(boolean param) { + throw new IllegalStateException(FAILURE_MESSAGE + param); + } +} diff --git a/src/test/java/com/epam/reportportal/junit5/features/issue/ParameterizedWithTwoIssueTest.java b/src/test/java/com/epam/reportportal/junit5/features/issue/ParameterizedWithTwoIssueTest.java new file mode 100644 index 0000000..4d156c4 --- /dev/null +++ b/src/test/java/com/epam/reportportal/junit5/features/issue/ParameterizedWithTwoIssueTest.java @@ -0,0 +1,40 @@ +/* + * Copyright 2024 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.epam.reportportal.junit5.features.issue; + +import com.epam.reportportal.annotations.Issue; +import com.epam.reportportal.annotations.TestFilter; +import com.epam.reportportal.annotations.TestParamFilter; +import com.epam.reportportal.junit5.IssueReportingTest; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +@ExtendWith(IssueReportingTest.TestExtension.class) +public class ParameterizedWithTwoIssueTest { + + public static final String FAILURE_MESSAGE = "This parameterized test is expected to fail: "; + public static final String ISSUE_MESSAGE = "This test is expected to fail"; + + @ParameterizedTest + @ValueSource(booleans = { true, false }) + @Issue(value = "ab001", comment = ISSUE_MESSAGE, filter = @TestFilter(param = { @TestParamFilter(valueStartsWith = "true") })) + @Issue(value = "pb001", comment = ISSUE_MESSAGE, filter = @TestFilter(param = { @TestParamFilter(valueStartsWith = "false") })) + public void failureTest(boolean param) { + throw new IllegalStateException(FAILURE_MESSAGE + param); + } +} diff --git a/src/test/java/com/epam/reportportal/junit5/features/issue/SimpleIssueTest.java b/src/test/java/com/epam/reportportal/junit5/features/issue/SimpleIssueTest.java new file mode 100644 index 0000000..6b754be --- /dev/null +++ b/src/test/java/com/epam/reportportal/junit5/features/issue/SimpleIssueTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2024 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.epam.reportportal.junit5.features.issue; + +import com.epam.reportportal.annotations.Issue; +import com.epam.reportportal.junit5.IssueReportingTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(IssueReportingTest.TestExtension.class) +public class SimpleIssueTest { + + public static final String FAILURE_MESSAGE = "This test is expected to fail"; + + @Test + @Issue(value = "pb001", comment = FAILURE_MESSAGE) + public void failureTest() { + throw new IllegalStateException(FAILURE_MESSAGE); + } +} diff --git a/src/test/java/com/epam/reportportal/junit5/features/issue/SimpleSkippedIssueTest.java b/src/test/java/com/epam/reportportal/junit5/features/issue/SimpleSkippedIssueTest.java new file mode 100644 index 0000000..c82655d --- /dev/null +++ b/src/test/java/com/epam/reportportal/junit5/features/issue/SimpleSkippedIssueTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2024 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.epam.reportportal.junit5.features.issue; + +import com.epam.reportportal.annotations.Issue; +import com.epam.reportportal.junit5.IssueReportingTest; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(IssueReportingTest.TestExtension.class) +public class SimpleSkippedIssueTest { + + public static final String FAILURE_MESSAGE = "This test is expected to fail"; + + @Test + @Issue(value = "pb001", comment = FAILURE_MESSAGE) + @Disabled + public void failureTest() { + throw new IllegalStateException(FAILURE_MESSAGE); + } +} diff --git a/src/test/java/com/epam/reportportal/junit5/features/issue/SimpleTwoIssuesTest.java b/src/test/java/com/epam/reportportal/junit5/features/issue/SimpleTwoIssuesTest.java new file mode 100644 index 0000000..562e2f8 --- /dev/null +++ b/src/test/java/com/epam/reportportal/junit5/features/issue/SimpleTwoIssuesTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2024 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.epam.reportportal.junit5.features.issue; + +import com.epam.reportportal.annotations.Issue; +import com.epam.reportportal.junit5.IssueReportingTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(IssueReportingTest.TestExtension.class) +public class SimpleTwoIssuesTest { + + public static final String FAILURE_MESSAGE = "This test is expected to fail"; + + @Test + @Issue(value = "ab001", comment = FAILURE_MESSAGE) + @Issue(value = "pb001", comment = FAILURE_MESSAGE) + public void failureTest() { + throw new IllegalStateException(FAILURE_MESSAGE); + } +} diff --git a/src/test/java/com/epam/reportportal/junit5/features/issue/TwoDynamicIssueTest.java b/src/test/java/com/epam/reportportal/junit5/features/issue/TwoDynamicIssueTest.java new file mode 100644 index 0000000..cda7051 --- /dev/null +++ b/src/test/java/com/epam/reportportal/junit5/features/issue/TwoDynamicIssueTest.java @@ -0,0 +1,26 @@ +package com.epam.reportportal.junit5.features.issue; + +import com.epam.reportportal.annotations.Issue; +import com.epam.reportportal.junit5.IssueReportingTest; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.DynamicTest.dynamicTest; + +@ExtendWith(IssueReportingTest.TestExtension.class) +public class TwoDynamicIssueTest { + public static final String FAILURE_MESSAGE = "This test is expected to fail"; + + @TestFactory + @Issue(value = "ab001", comment = FAILURE_MESSAGE) + Stream<DynamicTest> testForTestFactory() { + return Stream.of(dynamicTest("My dynamic test", () -> { + throw new IllegalStateException(FAILURE_MESSAGE); + }), dynamicTest("My dynamic test 2", () -> { + throw new IllegalStateException(FAILURE_MESSAGE); + })); + } +} diff --git a/src/test/java/com/epam/reportportal/junit5/features/issue/TwoDynamicTwoIssueTest.java b/src/test/java/com/epam/reportportal/junit5/features/issue/TwoDynamicTwoIssueTest.java new file mode 100644 index 0000000..442c6db --- /dev/null +++ b/src/test/java/com/epam/reportportal/junit5/features/issue/TwoDynamicTwoIssueTest.java @@ -0,0 +1,29 @@ +package com.epam.reportportal.junit5.features.issue; + +import com.epam.reportportal.annotations.Issue; +import com.epam.reportportal.annotations.TestFilter; +import com.epam.reportportal.annotations.TestNameFilter; +import com.epam.reportportal.junit5.IssueReportingTest; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.DynamicTest.dynamicTest; + +@ExtendWith(IssueReportingTest.TestExtension.class) +public class TwoDynamicTwoIssueTest { + public static final String FAILURE_MESSAGE = "This test is expected to fail"; + + @TestFactory + @Issue(value = "ab001", comment = FAILURE_MESSAGE, filter = { @TestFilter(name = @TestNameFilter(endsWith = "test")) }) + @Issue(value = "pb001", comment = FAILURE_MESSAGE, filter = { @TestFilter(name = @TestNameFilter(contains = "test 2")) }) + Stream<DynamicTest> testForTestFactory() { + return Stream.of(dynamicTest("My dynamic test", () -> { + throw new IllegalStateException(FAILURE_MESSAGE); + }), dynamicTest("My dynamic test 2", () -> { + throw new IllegalStateException(FAILURE_MESSAGE); + })); + } +} diff --git a/src/test/java/com/epam/reportportal/junit5/features/step/ManualStepReporterFeatureTest.java b/src/test/java/com/epam/reportportal/junit5/features/step/ManualStepReporterFeatureTest.java index 2539127..aaf2291 100644 --- a/src/test/java/com/epam/reportportal/junit5/features/step/ManualStepReporterFeatureTest.java +++ b/src/test/java/com/epam/reportportal/junit5/features/step/ManualStepReporterFeatureTest.java @@ -40,19 +40,22 @@ public class ManualStepReporterFeatureTest { private static final Logger LOGGER = LoggerFactory.getLogger(ManualStepReporterFeatureTest.class); @Test - public void manualStepTest() { + public void manualStepTest() throws InterruptedException { StepReporter stepReporter = Launch.currentLaunch().getStepReporter(); stepReporter.sendStep(FIRST_NAME); LOGGER.info("First info log of the first step"); LOGGER.info("Second info log of the first step"); + Thread.sleep(100); stepReporter.sendStep(SECOND_NAME); LOGGER.error("First error log of the second step"); + Thread.sleep(100); stepReporter.sendStep(ItemStatus.FAILED, THIRD_NAME, new File("pug/unlucky.jpg")); LOGGER.error("Second error log of the second step"); + Thread.sleep(100); stepReporter.finishPreviousStep(); LOGGER.error("Main test method error log");