Go-Style Table Tests in Java
Go Table Tests are a concise way to of representing unit test cases. This is a short post describing one way of emulating them.
After working in Java for a long time, a couple of years ago I joined a company that developed applications in Go. After getting the (relatively short) learning curve, I discovered that I enjoyed working in Go, as it made doing some things easier and more concise than Java. I’ve learned that some have strong opinions about these two languages. I’m in the middle, and it’s about suitability for use.
Table Drive Test is one idiom I got in the habit of using when working in Go that I missed when going back to Java. JUnit 5 provides the basic framework for this with support for Parameterized Tests, but may of the examples I found were overly verbose or simplistic — for example, the same assertion for each input.
In this post I want to share an approach that, while still a bit more verbose than it might be in Go, meets the requirements of:
Locality to the test
Easy to iterate through permutations
Has results and input closely related
This example tests a class that has an apply method that returns a boolean indicating whether lValue is greater than rValue.
package org.example;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
class GreaterThanTest {
// test case
record GreaterThanTestData(Integer lVal,
Integer rVal,
boolean want){}
// data provider
static Stream<GreaterThanTestData> data(){
return Stream.of(new GreaterThanTestData(1,2,false)
, new GreaterThanTestData(2,2,false)
, new GreaterThanTestData(4,3,true)
);
}
@ParameterizedTest()
@MethodSource("data")
void apply(GreaterThanTestData tt) {
GreaterThan<Integer> c = new GreaterThan<>();
boolean got = c.apply(tt.lVal, tt.rVal);
assertEquals( tt.want, got,String.format("compare %d, %d", tt.lVal, tt.rVal));
}
}
The key points are:
Test Data and provider are local to the test class
Test Data Class contains inputs and expectations.
I used some table test naming conventions from Go (tt
for the test object, want
for the expectation). Java records
aren’t quite as concise as Go structs
for this, but they work well enough.
Now all we need is simple IDE support to generate a test case template using providers.
Let me know if this is useful or how I can improve it (or if you think this approach has problems).
A friends pointed out that records might be the way to simplify the test case spec. I’ll give it a try and update the example if that works.