It’s been a while since I wrote code these days. Back in late April however I found myself in a Coding Dojo at the Düsseldorf Softwerkskammer meet-up working on the Mars Rover Kata. I have a story to share from that meeting. However, since I tried to reproduce the code we ended up with that night, and I decided to give JUnit5 (and Java8) a go for that, I ended up with a struggle.
Back in the days with JUnit4 I used the ParameterizedRunner quite often to use data-driven tests. I never remembered the signature of the @Parameters function, though. The Mars Rover Kata also includes some behavior that I wanted to run through a data-driven test, but how do you do that in JUnit5? I couldn’t find good answers for that on the Internet, so I decided to put my solution up here – probably for lots of critique.
Please note that I used JUnit 5.0.0-SNAPSHOT which is a later version than the alpha, but probably not the final one.
JUnit5 offers besides Java 8 capabilities some interesting new things. JUnit5 comes now with Extension capabilities where you may influence the test’s lifecycle and also ways to resolve parameters to your tests, and your test class constructors. And then there are TestFactories for DynamicTests. Woha, quite a lot new stuff.
First I tried stuff with parameter resolvers. But then I would have needed to keep track of the parameters, and I had to call the parameter resolver more than once. So, combining it with an extension might work? No, I couldn’t make that work. So, dynamic tests are the way to go.
So, here is an example for what I ended up with. We have a Direction class with a method called turnLeft(). The idea is if the Rover is headed NORTH, and turns left (by 90 degrees) then it will be facing WEST.
Some notes:
- I kept a collection of test data in a field in line 17. This is somewhat similar to the old way you annotated a function with @Parameters in JUnit4, even though you can now get rid of the Object[], and use a private test data class per test class. That at least seems to be the solution that I preferred.
- For the @TestFactory you have several possibilities. I decided to use the Stream return type here in line 28. As I haven’t programmed too much in Java 8, I am not sure whether my usage is appropriate here. The conversion of the testData from the Collection is quite straight-forward, I found.
- For each operation I wrapped the assertion in line 36 to avoid making the call to dynamicTest more convoluted than necessary. I also decided to generate a descriptive string for each test with the method in line 32. I think you can come up with better ways to generate the test descriptions. Wrapping the assertion on seemed unavoidable though. I especially didn’t like the usage of the lambda-expression together with the aggregate expression seems to make the line with dynamicTest (line 29) less readable than I would like to. I think there is more improvement possible.
- Note that you can have several @TestFactory methods on your test class. So when writing a test for turning right, you can provide another TestFactory and reuse the test data for that. I’ll leave that as an exercise for the inspired reader of my blog.
So, this is what I ended up with. I think there is still room for improvement, especially when you compare the result with stuff you might write in tools like Spock.
P.S.: I ran this through Marc Philipp – one of the JUnit5 originators – in an earlier version, and he told me that they will be working on a more elegant solution for data-driven tests, probably for one of the next releases of JUnit5.