diff --git a/build.gradle b/build.gradle index cb8d967..6f5af14 100644 --- a/build.gradle +++ b/build.gradle @@ -37,7 +37,7 @@ repositories { } dependencies { - api 'com.epam.reportportal:client-java:5.2.5' + api 'com.epam.reportportal:client-java:5.2.6' compileOnly "org.junit.jupiter:junit-jupiter-api:${junit_version}" implementation 'com.google.code.findbugs:jsr305:3.0.2' diff --git a/src/main/java/com/epam/reportportal/junit5/ReportPortalExtension.java b/src/main/java/com/epam/reportportal/junit5/ReportPortalExtension.java index eb5f950..429a12f 100644 --- a/src/main/java/com/epam/reportportal/junit5/ReportPortalExtension.java +++ b/src/main/java/com/epam/reportportal/junit5/ReportPortalExtension.java @@ -16,6 +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.attribute.Attributes; @@ -350,8 +351,9 @@ public void afterTestExecution(ExtensionContext context) { @Override public void testDisabled(ExtensionContext context, Optional reason) { if (Boolean.parseBoolean(System.getProperty("reportDisabledTests"))) { - String description = reason.orElse(createStepDescription(context)); - startTestItem(context, Collections.emptyList(), STEP, description, Calendar.getInstance().getTime()); + final ItemType itemType = STEP; + String description = reason.orElse(createStepDescription(context, itemType)); + startTestItem(context, Collections.emptyList(), itemType, description, Calendar.getInstance().getTime()); finishTest(context, SKIPPED); } } @@ -455,7 +457,7 @@ protected void startTestItem(ExtensionContext context, ItemType type) { * @param type a type of the item */ protected void startTestItem(ExtensionContext context, List arguments, ItemType type) { - startTestItem(context, arguments, type, createStepDescription(context), Calendar.getInstance().getTime()); + startTestItem(context, arguments, type, createStepDescription(context, type), Calendar.getInstance().getTime()); } /** @@ -822,8 +824,41 @@ protected String createConfigurationName(@Nonnull Class testClass, @Nonnull M * @return Test/Step Description being sent to ReportPortal */ @SuppressWarnings("unused") - protected String createStepDescription(ExtensionContext context) { - return ""; + protected String createStepDescription(ExtensionContext context, final ItemType itemType) { + String defaultValue = ""; + if (itemType != STEP) { + return defaultValue; + } + Optional optionalMethod = getOptionalTestMethod(context); + if (!optionalMethod.isPresent()){ + return defaultValue; + } + Method method = optionalMethod.get(); + + Description descriptionFromMethod = method.getAnnotation(Description.class); + if(descriptionFromMethod != null){ + return descriptionFromMethod.value(); + } + + Description descriptionFromClass = method.getDeclaringClass().getAnnotation(Description.class); + if(descriptionFromClass != null){ + return descriptionFromClass.value(); + } + + return defaultValue; + } + + private Optional getOptionalTestMethod(ExtensionContext context){ + Optional 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 parentContext = context.getParent(); + if(!parentContext.isPresent()){ + return Optional.empty(); + } + return parentContext.get().getTestMethod(); + } + return optionalMethod; } /** @@ -853,7 +888,8 @@ protected void reportSkippedStep(ReflectiveInvocationContext invocationC // to fix item ordering when @AfterEach starts in the same millisecond as skipped test skipStartTime = new Date(skipStartTime.getTime() - 1); } - startTestItem(context, invocationContext.getArguments(), STEP, createStepDescription(context), skipStartTime); + final ItemType itemType = STEP; + startTestItem(context, invocationContext.getArguments(), itemType, createStepDescription(context, itemType), skipStartTime); createSkippedSteps(context, throwable); FinishTestItemRQ finishRq = buildFinishTestItemRq(context, SKIPPED); finishRq.setIssue(Launch.NOT_ISSUE); diff --git a/src/test/java/com/epam/reportportal/junit5/DescriptionTest.java b/src/test/java/com/epam/reportportal/junit5/DescriptionTest.java new file mode 100644 index 0000000..2149716 --- /dev/null +++ b/src/test/java/com/epam/reportportal/junit5/DescriptionTest.java @@ -0,0 +1,148 @@ +package com.epam.reportportal.junit5; + +import com.epam.reportportal.junit5.features.description.*; +import com.epam.reportportal.junit5.util.TestUtils; +import com.epam.reportportal.service.Launch; +import com.epam.reportportal.util.test.CommonUtils; +import com.epam.ta.reportportal.ws.model.StartTestItemRQ; +import io.reactivex.Maybe; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.mockito.ArgumentCaptor; +import org.mockito.stubbing.Answer; + +import java.util.List; +import java.util.stream.Collectors; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +/** + * @author Oleksandr Fomenko + */ +public class DescriptionTest { + public static class TestExtension extends ReportPortalExtension { + static Launch LAUNCH; + + @Override + protected Launch getLaunch(ExtensionContext context) { + return LAUNCH; + } + } + + @BeforeEach + public void setupMock() { + TestExtension.LAUNCH = mock(Launch.class); + when(TestExtension.LAUNCH.startTestItem(any())).thenAnswer((Answer>) invocation -> CommonUtils.createMaybeUuid()); + when(TestExtension.LAUNCH.startTestItem(any(), + any() + )).thenAnswer((Answer>) invocation -> CommonUtils.createMaybeUuid()); + } + + @Test + void descriptionFromMethodAnnotationTest() { + TestUtils.runClasses(DescriptionAnnotatedMethodTest.class); + + Launch launch = TestExtension.LAUNCH; + + verify(launch, times(1)).startTestItem(any()); // Start parent Suite + + ArgumentCaptor captor = ArgumentCaptor.forClass(StartTestItemRQ.class); + verify(launch, times(1)).startTestItem(notNull(), captor.capture()); // Start a test + + StartTestItemRQ request = captor.getValue(); + assertThat(request.getDescription(), equalTo(DescriptionAnnotatedMethodTest.TEST_DESCRIPTION_METHOD)); + } + + @Test + void descriptionFromClassAnnotationTest() { + TestUtils.runClasses(DescriptionAnnotatedClassTest.class); + + Launch launch = TestExtension.LAUNCH; + + verify(launch, times(1)).startTestItem(any()); // Start parent Suite + + ArgumentCaptor captor = ArgumentCaptor.forClass(StartTestItemRQ.class); + verify(launch, times(1)).startTestItem(notNull(), captor.capture()); // Start a test + + StartTestItemRQ request = captor.getValue(); + assertThat(request.getDescription(), equalTo(DescriptionAnnotatedClassTest.TEST_DESCRIPTION_CLASS)); + } + + @Test + void descriptionFromBothMethodAndClassAnnotationTest() { + TestUtils.runClasses(DescriptionAnnotatedMethodAndClassTest.class); + + Launch launch = TestExtension.LAUNCH; + + verify(launch, times(1)).startTestItem(any()); // Start parent Suite + + ArgumentCaptor captor = ArgumentCaptor.forClass(StartTestItemRQ.class); + verify(launch, times(1)).startTestItem(notNull(), captor.capture()); // Start a test + + StartTestItemRQ request = captor.getValue(); + //from both annotations the expected one should be taken from the method + assertThat(request.getDescription(), equalTo(DescriptionAnnotatedMethodTest.TEST_DESCRIPTION_METHOD)); + } + + @Test + void descriptionFromMethodAnnotationDynamicTest() { + TestUtils.runClasses(DescriptionAnnotatedMethodDynamicTest.class); + + Launch launch = TestExtension.LAUNCH; + verify(launch, times(1)).startTestItem(any()); // Start parent Suite + + ArgumentCaptor captor = ArgumentCaptor.forClass(StartTestItemRQ.class); + verify(launch, times(2)).startTestItem(notNull(), captor.capture()); // Start a test + + List testStepsDescription = captor.getAllValues() + .stream() + .filter(e -> e.getType().equals(ItemType.STEP.name())) + .map(StartTestItemRQ::getDescription) + .collect(Collectors.toList()); + + assertThat(testStepsDescription, hasItem(DescriptionAnnotatedMethodDynamicTest.TEST_DESCRIPTION_DYNAMIC_METHOD)); + } + + @Test + void descriptionFromClassAnnotationDynamicTest() { + TestUtils.runClasses(DescriptionAnnotatedClassDynamicTest.class); + + Launch launch = TestExtension.LAUNCH; + verify(launch, times(1)).startTestItem(any()); // Start parent Suite + + ArgumentCaptor captor = ArgumentCaptor.forClass(StartTestItemRQ.class); + verify(launch, times(2)).startTestItem(notNull(), captor.capture()); // Start a test + + List testStepsDescription = captor.getAllValues() + .stream() + .filter(e -> e.getType().equals(ItemType.STEP.name())) + .map(StartTestItemRQ::getDescription) + .collect(Collectors.toList()); + + assertThat(testStepsDescription, hasItem(DescriptionAnnotatedClassDynamicTest.TEST_DESCRIPTION_DYNAMIC_CLASS)); + } + + @Test + void descriptionFromBothMethodAndClassAnnotationDynamicTest() { + TestUtils.runClasses(DescriptionAnnotatedMethodAndClassDynamicTest.class); + + Launch launch = TestExtension.LAUNCH; + verify(launch, times(1)).startTestItem(any()); // Start parent Suite + + ArgumentCaptor captor = ArgumentCaptor.forClass(StartTestItemRQ.class); + verify(launch, times(2)).startTestItem(notNull(), captor.capture()); // Start a test + + List testStepsDescription = captor.getAllValues() + .stream() + .filter(e -> e.getType().equals(ItemType.STEP.name())) + .map(StartTestItemRQ::getDescription) + .collect(Collectors.toList()); + //from both annotations the expected one should be taken from the method + assertThat(testStepsDescription, hasItem(DescriptionAnnotatedMethodDynamicTest.TEST_DESCRIPTION_DYNAMIC_METHOD)); + } +} diff --git a/src/test/java/com/epam/reportportal/junit5/features/description/DescriptionAnnotatedClassDynamicTest.java b/src/test/java/com/epam/reportportal/junit5/features/description/DescriptionAnnotatedClassDynamicTest.java new file mode 100644 index 0000000..f86947c --- /dev/null +++ b/src/test/java/com/epam/reportportal/junit5/features/description/DescriptionAnnotatedClassDynamicTest.java @@ -0,0 +1,22 @@ +package com.epam.reportportal.junit5.features.description; + +import com.epam.reportportal.annotations.Description; +import com.epam.reportportal.junit5.DescriptionTest; +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(DescriptionTest.TestExtension.class) +@Description(DescriptionAnnotatedClassDynamicTest.TEST_DESCRIPTION_DYNAMIC_CLASS) +public class DescriptionAnnotatedClassDynamicTest { + public static final String TEST_DESCRIPTION_DYNAMIC_CLASS = "My test description on the dynamic class"; + @TestFactory + + Stream testForTestFactory() { + return Stream.of(dynamicTest("My dynamic test", () -> System.out.println("Inside dynamic test"))); + } +} diff --git a/src/test/java/com/epam/reportportal/junit5/features/description/DescriptionAnnotatedClassTest.java b/src/test/java/com/epam/reportportal/junit5/features/description/DescriptionAnnotatedClassTest.java new file mode 100644 index 0000000..c8bd0ec --- /dev/null +++ b/src/test/java/com/epam/reportportal/junit5/features/description/DescriptionAnnotatedClassTest.java @@ -0,0 +1,15 @@ +package com.epam.reportportal.junit5.features.description; + +import com.epam.reportportal.annotations.Description; +import com.epam.reportportal.junit5.DescriptionTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@Description(DescriptionAnnotatedClassTest.TEST_DESCRIPTION_CLASS) +@ExtendWith(DescriptionTest.TestExtension.class) +public class DescriptionAnnotatedClassTest { + public static final String TEST_DESCRIPTION_CLASS = "My test description on the class"; + @Test + public void testDescriptionTest() { + } +} diff --git a/src/test/java/com/epam/reportportal/junit5/features/description/DescriptionAnnotatedMethodAndClassDynamicTest.java b/src/test/java/com/epam/reportportal/junit5/features/description/DescriptionAnnotatedMethodAndClassDynamicTest.java new file mode 100644 index 0000000..157986e --- /dev/null +++ b/src/test/java/com/epam/reportportal/junit5/features/description/DescriptionAnnotatedMethodAndClassDynamicTest.java @@ -0,0 +1,21 @@ +package com.epam.reportportal.junit5.features.description; + +import com.epam.reportportal.annotations.Description; +import com.epam.reportportal.junit5.DescriptionTest; +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(DescriptionTest.TestExtension.class) +@Description(DescriptionAnnotatedClassDynamicTest.TEST_DESCRIPTION_DYNAMIC_CLASS) +public class DescriptionAnnotatedMethodAndClassDynamicTest { + @TestFactory + @Description(DescriptionAnnotatedMethodDynamicTest.TEST_DESCRIPTION_DYNAMIC_METHOD) + Stream testForTestFactory() { + return Stream.of(dynamicTest("My dynamic test", () -> System.out.println("Inside dynamic test"))); + } +} diff --git a/src/test/java/com/epam/reportportal/junit5/features/description/DescriptionAnnotatedMethodAndClassTest.java b/src/test/java/com/epam/reportportal/junit5/features/description/DescriptionAnnotatedMethodAndClassTest.java new file mode 100644 index 0000000..461374b --- /dev/null +++ b/src/test/java/com/epam/reportportal/junit5/features/description/DescriptionAnnotatedMethodAndClassTest.java @@ -0,0 +1,15 @@ +package com.epam.reportportal.junit5.features.description; + +import com.epam.reportportal.annotations.Description; +import com.epam.reportportal.junit5.DescriptionTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(DescriptionTest.TestExtension.class) +@Description(DescriptionAnnotatedClassTest.TEST_DESCRIPTION_CLASS) +public class DescriptionAnnotatedMethodAndClassTest { + @Test + @Description(DescriptionAnnotatedMethodTest.TEST_DESCRIPTION_METHOD) + public void testDescriptionTest() { + } +} diff --git a/src/test/java/com/epam/reportportal/junit5/features/description/DescriptionAnnotatedMethodDynamicTest.java b/src/test/java/com/epam/reportportal/junit5/features/description/DescriptionAnnotatedMethodDynamicTest.java new file mode 100644 index 0000000..43659a3 --- /dev/null +++ b/src/test/java/com/epam/reportportal/junit5/features/description/DescriptionAnnotatedMethodDynamicTest.java @@ -0,0 +1,21 @@ +package com.epam.reportportal.junit5.features.description; + +import com.epam.reportportal.annotations.Description; +import com.epam.reportportal.junit5.DescriptionTest; +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(DescriptionTest.TestExtension.class) +public class DescriptionAnnotatedMethodDynamicTest { + public static final String TEST_DESCRIPTION_DYNAMIC_METHOD = "My test description on the dynamic method"; + @TestFactory + @Description(TEST_DESCRIPTION_DYNAMIC_METHOD) + Stream testForTestFactory() { + return Stream.of(dynamicTest("My dynamic test", () -> System.out.println("Inside dynamic test"))); + } +} diff --git a/src/test/java/com/epam/reportportal/junit5/features/description/DescriptionAnnotatedMethodTest.java b/src/test/java/com/epam/reportportal/junit5/features/description/DescriptionAnnotatedMethodTest.java new file mode 100644 index 0000000..84fb272 --- /dev/null +++ b/src/test/java/com/epam/reportportal/junit5/features/description/DescriptionAnnotatedMethodTest.java @@ -0,0 +1,15 @@ +package com.epam.reportportal.junit5.features.description; + +import com.epam.reportportal.annotations.Description; +import com.epam.reportportal.junit5.DescriptionTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(DescriptionTest.TestExtension.class) +public class DescriptionAnnotatedMethodTest { + public static final String TEST_DESCRIPTION_METHOD = "My test description on the method"; + @Test + @Description(TEST_DESCRIPTION_METHOD) + public void testDescriptionTest() { + } +}