Skip to content

Commit

Permalink
Merge pull request #129 from aykhangaffarov/RPP_LAST_ERROR_LOG_DESCRI…
Browse files Browse the repository at this point in the history
…PTION

Add last error in test description
  • Loading branch information
HardNorth committed Apr 19, 2024
2 parents 16e91d4 + fca68a0 commit f69ee7d
Show file tree
Hide file tree
Showing 6 changed files with 261 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
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.*;
import org.junit.jupiter.api.extension.*;
import org.opentest4j.TestAbortedException;
Expand Down Expand Up @@ -91,7 +92,8 @@ public class ReportPortalExtension
private final Map<ExtensionContext, Maybe<String>> idMapping = new ConcurrentHashMap<>();
private final Map<ExtensionContext, Maybe<String>> testTemplates = 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<>();
@Nonnull
protected Optional<Maybe<String>> getItemId(@Nonnull ExtensionContext context) {
return ofNullable(idMapping.get(context));
Expand Down Expand Up @@ -286,14 +288,16 @@ public <T> T interceptTestFactoryMethod(Invocation<T> invocation,
/**
* Returns a status of a test based on execution exception
*
* @param context JUnit's test context
* @param throwable test exception
* @return an {@link ItemStatus}
*/
@Nonnull
protected ItemStatus getExecutionStatus(@Nullable final Throwable throwable) {
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;
}
Expand All @@ -320,7 +324,7 @@ public void interceptDynamicTest(Invocation<Void> invocation, DynamicTestInvocat
invocation.proceed();
finishTest(extensionContext, PASSED);
} catch (Throwable throwable) {
finishTest(extensionContext, getExecutionStatus(throwable));
finishTest(extensionContext, getExecutionStatus(extensionContext, throwable));
throw throwable;
}
}
Expand All @@ -340,7 +344,7 @@ public void interceptTestTemplateMethod(Invocation<Void> invocation,
*/
@Nonnull
protected ItemStatus getExecutionStatus(@Nonnull final ExtensionContext context) {
return context.getExecutionException().map(this::getExecutionStatus).orElse(PASSED);
return context.getExecutionException().map(t -> getExecutionStatus(context, t)).orElse(PASSED);
}

@Override
Expand Down Expand Up @@ -368,6 +372,7 @@ public void testFailed(ExtensionContext context, Throwable cause) {
if(failedClassInits.contains(parent)) {
startTestItem(context, STEP);
sendStackTraceToRP(cause);
testThrowable.put(context, cause);
finishTest(context, FAILED);
}
});
Expand Down Expand Up @@ -420,7 +425,7 @@ private void finishBeforeAfter(Invocation<Void> invocation, ExtensionContext con
try {
invocation.proceed();
} catch (Throwable throwable) {
finishBeforeAfter(context, id, getExecutionStatus(throwable));
finishBeforeAfter(context, id, getExecutionStatus(context, throwable));
throw throwable;
}
finishBeforeAfter(context, id, PASSED);
Expand Down Expand Up @@ -757,8 +762,16 @@ protected void createSkippedSteps(ExtensionContext context, Throwable cause) {
@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)));
rq.setDescription(description);
}
ofNullable(status).ifPresent(s -> rq.setStatus(s.name()));
rq.setEndTime(Calendar.getInstance().getTime());
testThrowable.remove(context);
return rq;
}

Expand Down
151 changes: 151 additions & 0 deletions src/test/java/com/epam/reportportal/junit5/ErrorLastLogTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package com.epam.reportportal.junit5;

import com.epam.reportportal.junit5.features.lasterrorlog.ErrorLastLogFeatureWithAssertionErrorTest;
import com.epam.reportportal.junit5.features.lasterrorlog.ErrorLastLogFeatureWithAssertionPassedTest;
import com.epam.reportportal.junit5.features.lasterrorlog.ErrorLastLogFeatureWithExceptionTest;
import com.epam.reportportal.junit5.features.lasterrorlog.ErrorLastLogFeatureWithStepTest;
import com.epam.reportportal.junit5.util.TestUtils;
import com.epam.reportportal.service.Launch;
import com.epam.reportportal.service.ReportPortal;
import com.epam.reportportal.service.ReportPortalClient;
import com.epam.ta.reportportal.ws.model.FinishTestItemRQ;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.platform.engine.TestExecutionResult;
import org.junit.platform.launcher.TestExecutionListener;
import org.junit.platform.launcher.TestIdentifier;
import org.mockito.ArgumentCaptor;

import java.util.Deque;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;

import static com.epam.reportportal.util.test.CommonUtils.namedId;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.startsWith;
import static org.mockito.Mockito.*;

@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 {

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())));

}

@Override
protected Launch getLaunch(ExtensionContext context) {
return launch.get();
}

}

public static class Listener implements TestExecutionListener {
public Deque<TestExecutionResult> results = new ConcurrentLinkedDeque<>();

@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);

ReportPortalClient client = ErrorDescriptionTestExtension.client.get();

ArgumentCaptor<FinishTestItemRQ> finishTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class);
verify(client, timeout(1000).atLeastOnce()).finishTestItem(same(testMethodUuid), finishTestCaptor.capture());


List<FinishTestItemRQ> finishTests = finishTestCaptor.getAllValues();

FinishTestItemRQ testCaseWithException = finishTests.get(0);

assertThat(testCaseWithException.getDescription(), startsWith(String.format(testErrorMessagePattern, "", testExceptionMessage)));
assertThat(testCaseWithException.getStatus(), equalTo(failedStatus));

}

@Test
public void verify_last_error_log_step() {
ErrorDescriptionTestExtension.init();
Listener listener = new Listener();
TestUtils.runClasses(listener, ErrorLastLogFeatureWithStepTest.class);

ReportPortalClient client = ErrorDescriptionTestExtension.client.get();

ArgumentCaptor<FinishTestItemRQ> finishTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class);
verify(client, timeout(1000).atLeastOnce()).finishTestItem(same(testMethodUuid), finishTestCaptor.capture());

List<FinishTestItemRQ> finishTests = finishTestCaptor.getAllValues();

FinishTestItemRQ testCaseWithDescriptionAndStepError = finishTests.get(0);

assertThat(testCaseWithDescriptionAndStepError.getDescription(), startsWith(String.format(testErrorMessagePattern, stepDescriptionMessage, exceptionStepErrorMessage)));
assertThat(testCaseWithDescriptionAndStepError.getStatus(), equalTo(failedStatus));
}

@Test
public void verify_last_error_log_assertion_error() {
ErrorDescriptionTestExtension.init();
Listener listener = new Listener();
TestUtils.runClasses(listener, ErrorLastLogFeatureWithAssertionErrorTest.class);

ReportPortalClient client = ErrorDescriptionTestExtension.client.get();

ArgumentCaptor<FinishTestItemRQ> finishTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class);
verify(client, timeout(1000).atLeastOnce()).finishTestItem(same(testMethodUuid), finishTestCaptor.capture());

List<FinishTestItemRQ> finishTests = finishTestCaptor.getAllValues();

FinishTestItemRQ testCaseAssertException = finishTests.get(0);

assertThat(testCaseAssertException.getDescription(), startsWith(String.format(testErrorMessagePattern, assertDescriptionMessage, assertErrorMessage)));
assertThat(testCaseAssertException.getStatus(), equalTo(failedStatus));
}

@Test
public void verify_last_error_log_assertion_passed() {
ErrorDescriptionTestExtension.init();
Listener listener = new Listener();
TestUtils.runClasses(listener, ErrorLastLogFeatureWithAssertionPassedTest.class);

ReportPortalClient client = ErrorDescriptionTestExtension.client.get();

ArgumentCaptor<FinishTestItemRQ> finishTestCaptor = ArgumentCaptor.forClass(FinishTestItemRQ.class);
verify(client, timeout(1000).atLeastOnce()).finishTestItem(same(testMethodUuid), finishTestCaptor.capture());

List<FinishTestItemRQ> finishTests = finishTestCaptor.getAllValues();

FinishTestItemRQ testCaseWithDescriptionAndPassed = finishTests.get(0);

assertThat(testCaseWithDescriptionAndPassed.getStatus(), equalTo(passedStatus));

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.epam.reportportal.junit5.features.lasterrorlog;

import com.epam.reportportal.annotations.Description;
import com.epam.reportportal.junit5.ErrorLastLogTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;


@ExtendWith(ErrorLastLogTest.ErrorDescriptionTestExtension.class)
public class ErrorLastLogFeatureWithAssertionErrorTest {

@Test
@Description("0 and 1 is not equal")
public void testWithAssertException() {
Assertions.assertEquals(0, 1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.epam.reportportal.junit5.features.lasterrorlog;

import com.epam.reportportal.annotations.Description;
import com.epam.reportportal.annotations.Step;
import com.epam.reportportal.junit5.ErrorLastLogTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(ErrorLastLogTest.ErrorDescriptionTestExtension.class)
public class ErrorLastLogFeatureWithAssertionPassedTest {

@Test
@Description("successful test")
public void testWithDescriptionAndPassed() {
login();
Assertions.assertTrue(true);
}

@Step
public void login() {
System.out.println("Login successful");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.epam.reportportal.junit5.features.lasterrorlog;

import com.epam.reportportal.junit5.ErrorLastLogTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(ErrorLastLogTest.ErrorDescriptionTestExtension.class)
public class ErrorLastLogFeatureWithExceptionTest {


@Test
public void testWithException() {
throw new RuntimeException("Critical error");
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.epam.reportportal.junit5.features.lasterrorlog;

import com.epam.reportportal.annotations.Description;
import com.epam.reportportal.annotations.Step;
import com.epam.reportportal.junit5.ErrorLastLogTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import java.util.NoSuchElementException;

@ExtendWith(ErrorLastLogTest.ErrorDescriptionTestExtension.class)
public class ErrorLastLogFeatureWithStepTest {

@Test
@Description("Login issue")
public void testWithStepError() {
enterCredentials();
System.out.println("Username is not correct");
loginWithException();
Assertions.assertTrue(Boolean.TRUE);
}

@Step
@Description("Credentials entered")
public void enterCredentials() {
Assertions.assertTrue(Boolean.TRUE);
}
@Step
public void loginWithException() {
throw new NoSuchElementException("Error message");
}
}

0 comments on commit f69ee7d

Please sign in to comment.