How to get the exception that was thrown when a Cucumber test failed in Java?

前端 未结 7 741
攒了一身酷
攒了一身酷 2021-01-13 02:07

I can perform actions on test failure by using:

@After
public void afterTest(Scenario scenario) {
    if (scenario.isFailed()) {
        /*Do stuff*/
    }
}         


        
7条回答
  •  再見小時候
    2021-01-13 02:46

    After a lot of experimentation I now removed the Before/After-Annotations and rely on Cucumber-Events instead. They contain the TestCase (which is what the Scenario-class wraps) and a Result where you can call getError(); to get the Throwable.

    Here is a simple example to get it working

    import io.cucumber.plugin.EventListener;
    import io.cucumber.plugin.event.EventPublisher;
    import io.cucumber.plugin.event.Result;
    import io.cucumber.plugin.event.Status;
    import io.cucumber.plugin.event.TestCase;
    import io.cucumber.plugin.event.TestCaseFinished;
    import io.cucumber.plugin.event.TestCaseStarted;
    import org.openqa.selenium.WebDriver;
    
    public class TestCaseListener implements EventListener {
        @Override
        public void setEventPublisher(final EventPublisher publisher) {
            publisher.registerHandlerFor(TestCaseStarted.class, this::onTestCaseStarted);
            publisher.registerHandlerFor(TestCaseFinished.class, this::onTestCaseFinished);
        }
    
        public void onTestCaseStarted(TestCaseStarted event) {
            TestCase testCase = event.getTestCase();
            System.out.println("Starting " + testCase.getName());
    
            // Other stuff you did in your @Before-Method.
            // ...
        }
    
        private void onTestCaseFinished(final TestCaseFinished event) {
            TestCase testCase = event.getTestCase();
            System.out.println("Finished " + testCase.getName());
    
            Result result = event.getResult();
            if (result.getStatus() == Status.FAILED) {
                final Throwable error = result.getError();
                error.printStackTrace();
            }
    
            // Other stuff you did in your @After-Method.
            // ...
        }
    }
    

    All that's left to do is to register this class as a Cucumber-Plugin. I did this by modifying my @CucumberOptions-annotation:

    @CucumberOptions(plugin = {"com.example.TestCaseListener"})
    

    I find this much cleaner than all of this reflection-madness, however it requires a lot more code-changes.

    Edit

    I don't know why, but this caused a lot of tests to randomly fail in a multithreaded environment. I tried to figure it out, but now also use the ugly reflections mentioned in this thread:

    public class SeleniumUtils {
    private static final Logger log = LoggerFactory.getLogger(SeleniumUtils.class);
    
    private static final Field field = FieldUtils.getField(Scenario.class, "delegate", true);
    private static Method getError;
    
    public static Throwable getError(Scenario scenario) {
        try {
            final TestCaseState testCase = (TestCaseState) field.get(scenario);
            if (getError == null) {
                getError = MethodUtils.getMatchingMethod(testCase.getClass(), "getError");
                getError.setAccessible(true);
            }
            return (Throwable) getError.invoke(testCase);
        } catch (Exception e) {
            log.warn("error receiving exception", e);
        }
        return null;
    }
    }
    

提交回复
热议问题