From 102bb2ea83a8f0131563780ae0d7fa80d4f44585 Mon Sep 17 00:00:00 2001 From: a-simeshin Date: Sat, 19 Apr 2025 08:46:08 +0300 Subject: [PATCH] Fix Steps bubbling in assertj aspect --- .../qameta/allure/assertj/AllureAspectJ.java | 39 +++++++++++++++---- .../allure/assertj/AllureAspectJTest.java | 27 +++++++++++++ .../io/qameta/allure/AllureLifecycle.java | 13 +++++++ .../io/qameta/allure/AllureLifecycleTest.java | 32 ++++++++++----- 4 files changed, 93 insertions(+), 18 deletions(-) diff --git a/allure-assertj/src/main/java/io/qameta/allure/assertj/AllureAspectJ.java b/allure-assertj/src/main/java/io/qameta/allure/assertj/AllureAspectJ.java index 12879117..1c0903f8 100644 --- a/allure-assertj/src/main/java/io/qameta/allure/assertj/AllureAspectJ.java +++ b/allure-assertj/src/main/java/io/qameta/allure/assertj/AllureAspectJ.java @@ -27,10 +27,10 @@ import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; -import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -88,12 +88,9 @@ public void logAssertCreation(final JoinPoint joinPoint) { @Before("anyAssert()") public void stepStart(final JoinPoint joinPoint) { - final MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); - final String uuid = UUID.randomUUID().toString(); - final String name = joinPoint.getArgs().length > 0 - ? String.format("%s \'%s\'", methodSignature.getName(), arrayToString(joinPoint.getArgs())) - : methodSignature.getName(); + final String name = getStepName(joinPoint); + if (isStartBubbling(name)) return; final StepResult result = new StepResult() .setName(name); @@ -102,7 +99,10 @@ public void stepStart(final JoinPoint joinPoint) { } @AfterThrowing(pointcut = "anyAssert()", throwing = "e") - public void stepFailed(final Throwable e) { + public void stepFailed(final JoinPoint joinPoin, final Throwable e) { + final String name = getStepName(joinPoin); + if (isEndBubbling(name)) return; + getLifecycle().updateStep(s -> s .setStatus(getStatus(e).orElse(Status.BROKEN)) .setStatusDetails(getStatusDetails(e).orElse(null))); @@ -110,7 +110,10 @@ public void stepFailed(final Throwable e) { } @AfterReturning(pointcut = "anyAssert()") - public void stepStop() { + public void stepStop(final JoinPoint joinPoin) { + final String name = getStepName(joinPoin); + if (isEndBubbling(name)) return; + getLifecycle().updateStep(s -> s.setStatus(Status.PASSED)); getLifecycle().stopStep(); } @@ -133,4 +136,24 @@ private static String arrayToString(final Object... array) { .map(ObjectUtils::toString) .collect(Collectors.joining(" ")); } + + private String getStepName(JoinPoint joinPoint) { + return joinPoint.getArgs().length > 0 + ? String.format("%s \'%s\'", joinPoint.getSignature().getName(), arrayToString(joinPoint.getArgs())) + : joinPoint.getSignature().getName(); + } + + private boolean isStartBubbling(String stepName) { + final Optional lastResult = getLifecycle().getCurrentStepResult(); + if (!lastResult.isPresent()) return false; + if (lastResult.get().getStop() != null) return false; + return stepName.equals(lastResult.get().getName()); + } + + private boolean isEndBubbling(String stepName) { + final Optional lastResult = getLifecycle().getCurrentStepResult(); + if (!lastResult.isPresent()) return true; + if (lastResult.get().getStop() != null) return true; + return !stepName.equals(lastResult.get().getName()); + } } diff --git a/allure-assertj/src/test/java/io/qameta/allure/assertj/AllureAspectJTest.java b/allure-assertj/src/test/java/io/qameta/allure/assertj/AllureAspectJTest.java index 57ddef24..6e858458 100644 --- a/allure-assertj/src/test/java/io/qameta/allure/assertj/AllureAspectJTest.java +++ b/allure-assertj/src/test/java/io/qameta/allure/assertj/AllureAspectJTest.java @@ -24,9 +24,13 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.List; +import java.util.Map; import static io.qameta.allure.test.RunUtils.runWithinTestContext; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatList; +import static org.assertj.core.api.MapAssert.assertThatMap; /** * @author charlie (Dmitry Baev). @@ -121,4 +125,27 @@ void softAssertions() { .extracting(StepResult::getName) .contains("as 'Test description []'", "isEqualTo '26'"); } + + @Test + void preventListAssertionsBubblingTest() { + final AllureResults results = runWithinTestContext( + () -> assertThatList(List.of("value1")).isEqualTo(List.of("value1", "value2")), + AllureAspectJ::setLifecycle); + + assertThat(results.getTestResults()).isNotEmpty(); + assertThat(results.getTestResults().get(0).getSteps()).hasSize(2); + assertThat(results.getTestResults().get(0).getSteps().get(1).getSteps()).isEmpty(); + } + + @Test + void preventMapAssertionsBubblingTest() { + final AllureResults results = runWithinTestContext( + () -> assertThatMap(Map.of("key1", "value1")) + .isEqualTo(Map.of("key1", "value1", "key2", "value2")), + AllureAspectJ::setLifecycle); + + assertThat(results.getTestResults()).isNotEmpty(); + assertThat(results.getTestResults().get(0).getSteps()).hasSize(2); + assertThat(results.getTestResults().get(0).getSteps().get(1).getSteps()).isEmpty(); + } } diff --git a/allure-java-commons/src/main/java/io/qameta/allure/AllureLifecycle.java b/allure-java-commons/src/main/java/io/qameta/allure/AllureLifecycle.java index bea1d4dc..8c5462c3 100644 --- a/allure-java-commons/src/main/java/io/qameta/allure/AllureLifecycle.java +++ b/allure-java-commons/src/main/java/io/qameta/allure/AllureLifecycle.java @@ -299,6 +299,19 @@ public Optional getCurrentTestCaseOrStep() { return threadContext.getCurrent(); } + /** + * Return optional current step result object. + * + * @return the optional wrapper of the current StepResult object. + */ + public Optional getCurrentStepResult() { + final Optional currentUuid = threadContext.getCurrent(); + if (!currentUuid.isPresent()) { + return Optional.empty(); + } + return storage.getStep(currentUuid.get()); + } + /** * Sets specified test case uuid as current. Note that * test case with such uuid should be created and existed in storage, otherwise diff --git a/allure-java-commons/src/test/java/io/qameta/allure/AllureLifecycleTest.java b/allure-java-commons/src/test/java/io/qameta/allure/AllureLifecycleTest.java index 0a06dd32..6c633aee 100644 --- a/allure-java-commons/src/test/java/io/qameta/allure/AllureLifecycleTest.java +++ b/allure-java-commons/src/test/java/io/qameta/allure/AllureLifecycleTest.java @@ -15,11 +15,8 @@ */ package io.qameta.allure; +import io.qameta.allure.model.*; import io.qameta.allure.model.Attachment; -import io.qameta.allure.model.FixtureResult; -import io.qameta.allure.model.StepResult; -import io.qameta.allure.model.TestResult; -import io.qameta.allure.model.TestResultContainer; import io.qameta.allure.test.AllureResults; import io.qameta.allure.test.RunUtils; import org.junit.jupiter.api.BeforeEach; @@ -30,12 +27,7 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArrayList; @@ -43,10 +35,12 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; import java.util.stream.Collectors; import static io.qameta.allure.Allure.addStreamAttachmentAsync; +import static io.qameta.allure.test.RunUtils.runWithinTestContext; import static io.qameta.allure.test.TestData.randomId; import static io.qameta.allure.test.TestData.randomName; import static io.qameta.allure.test.TestData.randomString; @@ -488,4 +482,22 @@ public Void call() { return null; } } + + @Test + void getCurrentStepResultTest() { + final AtomicReference lifecycle = new AtomicReference<>(); + runWithinTestContext(() -> { + assertThat(lifecycle.get().getCurrentStepResult()).isEmpty(); + + final StepResult testStepResult = new StepResult(); + testStepResult.setName("test step"); + testStepResult.setStatus(Status.PASSED); + + lifecycle.get().startStep(UUID.randomUUID().toString(), testStepResult); + assertThat(lifecycle.get().getCurrentStepResult()).isPresent().isEqualTo(testStepResult); + + lifecycle.get().stopStep(); + assertThat(lifecycle.get().getCurrentStepResult()).isEmpty(); + }, lifecycle::set); + } }