allure-framework / allure-java

Allure integrations for Java test frameworks
Apache License 2.0
341 stars 218 forks source link

🐞: Allure.getLifecycle().updateStep() doesn't update the status in the report #1020

Closed TripleG closed 3 months ago

TripleG commented 3 months ago

What happened?

I am trying to fail a method annotated with @Step which contains sub-steps:

@Test
public void test() {
    stepMethod1();
}

@Step("Step 1")
public void stepMethod1() {
    //Trying to make the current "Step 1" step failed
    Allure.getLifecycle().updateStep(stepResult ->
            stepResult.setStatus(Status.FAILED));

    Allure.step("first step", Status.FAILED);
}

Step 1 is shown as green/passed, although I explicitly make it failed through updateStep():

image

What Allure Integration are you using?

allure-testng

What version of Allure Integration you are using?

2.25.0

What version of Allure Report you are using?

2.25.0

Code of Conduct

baev commented 3 months ago

Both annotated steps (@Step) and lambda steps (Allure.step(name, step -> ...)) change the status after the method/lambda execution is finished. So, if no exception is thrown, the status will be set to pass.

To workaround that, you have two options

  1. Use lifecycle API and implement your own decision-making code:
final String uuid = UUID.randomUUID().toString();
getLifecycle().startStep(uuid, new StepResult().setName("step"));

try {
    final T result = runnable.run(new DefaultStepContext(uuid));
    getLifecycle().updateStep(uuid, step -> {
        // or simply set Status.PASSED only if no status set
        final Optional<StepResult> mostFailedStep = step.getSteps().stream()
                .min(Comparator.comparing(StepResult::getStatus));

        if (mostFailedStep.isPresent()) {
            step.setStatus(mostFailedStep.get().getStatus());
        } else {
            step.setStatus(Status.PASSED);
        }

    });
    return result;
} catch (Throwable throwable) {
    getLifecycle().updateStep(s -> s
            .setStatus(getStatus(throwable).orElse(Status.BROKEN))
            .setStatusDetails(getStatusDetails(throwable).orElse(null)));
    throw ExceptionUtils.sneakyThrow(throwable);
} finally {
    getLifecycle().stopStep(uuid);
}

The example aboce can be used via custom aspect or as lambda-step function.

  1. Use TestLifecycleListener or StepLifecycleListener and do the required step/test status modifications.
TripleG commented 3 months ago

Hi @baev!

I am a bit confused by:

Both annotated steps (@Step) and lambda steps (Allure.step(name, step -> ...)) change the status after the method/lambda execution is finished. So, if no exception is thrown, the status will be set to pass.

Does this mean that the updateStep() is usable for mutating some attributes of the step, but not the status?

baev commented 3 months ago

I am a bit confused by:

Both annotated steps (@step) and lambda steps (Allure.step(name, step -> ...)) change the status after the method/lambda execution is finished. So, if no exception is thrown, the status will be set to pass.

Does this mean that the updateStep() is usable for mutating some attributes of the step, but not the status?

@Step annotation and lambda steps Allure.step(name, step -> ...) are high level API around Lifecycle API (getLifecycle().updateStep etc). getLifecycle().updateStep updates status attribute without any issues. The reason it not work with @Step annotation is that the way @Step aspect is implemented — it calls getLifecycle().updateStep and sets status, based on exit code (finished with or without exception).