When JUnit 4.1 was released last year, they added a nice feature that has gone mostly unnoticed.
RSpec envy
Consider this archetypical RSpec example in Ruby. One class, Stack, being tested in two different scenarios (empty and non-empty):
describe Stack, " (empty)" do
before(:each) do
@stack = Stack.new
end
it "should have zero size" do
@stack.size.should == 0
end
# ...
end
describe Stack, " (non-empty)" do
before(:each) do
@stack = Stack.new
@stack.push 'x'
end
it "should have size greater than zero" do
@stack.size.should > 0
end
# ...
end
Doing the same thing in JUnit would require us to either create two different classes, which makes our tests hard to follow, or to abandon the “before”-methods and initializing at the start of each test method. Other tools, like JDave, solve this by having an inner class for each scenario, but for various reasons JDave isn’t the solution for me.
Enclosed to the rescue
When browsing the JUnit source code, my colleague Rickard stumbled on the org.junit.runners.Enclosed class. Apparently it’s been part of JUnit since 4.1 released in 2006. Enclosed is a test runner that runs all the inner classes of a class as tests. It works perfectly within IntelliJ and other tools. Now you can have Rspec-style testing in JUnit, almost. Behold its goodness:
@RunWith(org.junit.runners.Enclosed.class)
public class StackTest {
public static class EmptyStack {
private Stack stack;
@Before
public void before() {
stack = new Stack();
}
@Test
public void shouldHaveZeroSize() {
assertEquals(0, stack.size());
}
// ...
}
public static class NonEmptyStack {
private Stack stack;
@Before
public void before() {
stack = new Stack();
stack.push("x");
}
@Test
public void shouldHaveSizeGreaterThanZero() {
assertTrue(stack.size() > 0);
}
// ...
}
}
Update: Enclosed was moved to junit.experimental.runners.Enclosed in later JUnit versions.
Hmm, this example seems sort of artificial to me as it could be easily rewritten using two test methods.
Yes, it is artificial, that’s what makes it an example.
Seriously though, in real code you’d have a lot of test methods on each scenario, so you wouldn’t want to repeat the setup code in every method.