Extending the Cucumber Test Lifecycle
Overview
This article is about two things:
- How do I make the beforeAll and afterAll lifecycle events happen in Cucumber?
- How can I use TestContainers to set up the system under test before a Cucumber test runs?
No, YOU’RE trying to do SEO on YOUR blog.
That Cucumber Lifecycle…
Cucumber’s lifecycle includes:
- Before hook – before each scenario
- After hook – after each scenario
- Before step
- After step
And that’s about it… you can conditionally run hooks.
What if there’s a Grand Lifecycle?
You mean, what if you need to start up the system under test on a per test run basis, not on a per scenario basis? Or do some post testing cleanup?
It seems that this is not covered by cucumber itself.
Life has a way of needing this sort of feature.
Is there a way to run Before All and After All in Cucumber?
Assuming you’re using JUnit 4, then the following will apply. I would expect a JUnit5 equivalent, but have not tested it.
What you need to know:
- The Cucumber JUnit runner is based on JUnit4
ParentRunner
- This class has support for the JUnit
@Rule
annotation. In particular@ClassRule
- While Cucumber itself has no support for the additional lifecycle, it turns out the internals of JUnit4 will honour
@ClassRule
rules - So if you wanted to create some behaviour around the execution of your tests, you need to create a
TestRule
object as astatic
field of the cucumber suite class and add@ClassRule
to it.
A Real Life Scenario
We had an app which needed to be deployed inside docker in order to run our automation pack. We could use TestContainers to achieve this before all tests ran, and that would also tidy up the docker resources at the end of the test run.
As a test containers container is, itself, a TestRule
it was pretty trivial to inject one into the test:
1 2 3 4 5 6 7 8 9 | @RunWith (Cucumber. class ) @CucumberOptions (...) public class TestRunner { @ClassRule static GenericContainer REDIS = new GenericContainer<>( "redis:5.0.3-alpine" ) .withExposedPorts( 6379 ); // obviously we weren't testing redis, but this gives you the idea of a container } |
Inside the glue code, we could then reach back to the global object to ask it for the correct port number (as TestContainers puts containers on random exposed ports from the given logical port).
It’s That Easy
With this trick of using test rules, you can have that missing Cucumber lifecycle hook, and you can integrate a TestContainers element into the test.
Except… in real life I had a much more complex thing to do, so built a custom composite test rule to weave together the whole process, with the same use of @ClassRule
to link it into the lifecycle.
And if you’re in JUnit5, think JUnit5 extensions, rather than rules, and there’ll be a way you can produce a custom extension to do much the same thing, or just get your extension annotations in the right order, and JUnit5 may do it all for you.
Published on Java Code Geeks with permission by Ashley Frieze, partner at our JCG program. See the original article here: Extending the Cucumber Test Lifecycle Opinions expressed by Java Code Geeks contributors are their own. |