This is not going to be the case when it comes to testing, however. At the very least, we're going to use JUnit or TestNG (we used testng in the previous version, we've switched to JUnit for 3.0). Up until recently, we worked hard to eliminate the need for a mocking framework - the driver is not a large application with interacting services, most stuff can be tested either as an integration test or with very simple stubs.
Recently I was working on the serialisation layer - we're making quite big changes to the model for encoding and decoding between BSON and Java, we're hoping this will simplify our lives but also make things a lot easier for the ODMs (Object-Document Mappers) and third party libraries. At this level, it makes a lot of sense to introduce mocks - I want to ensure particular methods are called on the writer, for example, I don't want to check actual byte values, that's not going to be very helpful for documentation (although there is a level where that is a sensible thing to do).
We started using JMock to begin with, it's what I've been using for a while, and it gave us what we wanted - a simple mocking framework (I tried Mockito too, but I'm not so used to the failure messages, so I found it really hard to figure out what was wrong when a test failed).
I knew from my spies at LMAX that there's some Groovy test framework called Spock that is awesome, apparently, but I immediately discarded it - I feel very strongly that tests are documentation, and since the users of the Java driver are largely Java developers, I felt like introducing tests in a different language was an added complexity we didn't need.
Then I went to GeeCON, and my ex-colleague Israel forced me to go to the talk on Spock. And I realised just how wrong I had been. Far from adding complexity, here was a lovely, descriptive way of writing tests. It's flexible, and yet structured enough get you thinking in a way that should create good tests.
Since we're already using gradle, which is Groovy as well, we decided it was worth a spike to see if Spock would give us any benefits.
During the spike I converted a selection of our tests to Spock tests to see what it looks like on a real codebase. I had very specific things I wanted to try out:
- Data driven testing
In the talk I also saw useful annotation like
@Requires, which I'm pretty sure we're going to use, but I don't think it's made it into a build yet.
So, get this, I'm going to write a blog post with Actual Code in. Yeah, I know, you all thought I was just a poncy evangelist these days and didn't do any real coding any more.
First up, Mocking
So, as I said, I have a number of tests checking that encoding of Java objects works the way we expect. The easiest way to test this is to mock our BSONWriter class to ensure that the right interactions are happening against it. This is a nice way to check that when you give an encoder a particular set of data, it gets serialised in the way BSON expects. These tests ended up looking something like this:
So that's quite nice, my test checks that given a List of Strings, they get serialised correctly. What's not great is some of the setup overhead:
Obviously some of the things there are going to be ringing some people's alarm bells, but let's assume for a minute that all decisions were taken carefully and that pros and cons were weighed accordingly.
- Mocking concrete classes is not pretty in JMock, just look at that
- We're using the
JUnitRuleMockery, which appears to be Best Practice (and means you're less likely to forget the
@RunWith(JMock.class)annotation), but checkstyle hates it - Public Fields Are Bad as we all know.
I converted this test to a Spock test. Groovy purists will notice that it's still very Java-y, and that's intentional - I want these tests, at least at this stage while we're getting used to it, to be familiar to Java programmers, our main audience.
Some initial observations:
- It's a really simple thing, but I like having the
@Subjectannotation on the thing you're testing. In theory it should be obvious which of your fields or variables is the subject under test, but in practice that's not always true.
- Although it freaks me out as someone who's been doing Java for the last 15 years, I really like the String for method name - although in this case it's the same as the JMock/JUnit equivalent, it gives a lot more flexibility for describing the purpose of this test.
- Mocking is painless, with a simple call to
Mock(), even though we're still mocking concrete classes (this is done simply by adding cglib and obgenesis to the dependencies).
- I love that the phases of Spock (
setup: when: then:) document the different parts of the test while also being the useful magic keywords which tell Spock how to run the test. I know other frameworks provide this, but we've been working with JUnit and I've been in the habit of commenting my steps with
//given //when //then.
- Thanks to Groovy, creation of lists is less boiler plate (line 9). Not a big deal, but just makes it easier to read.
- I've got very used to the way expectations are set up in JMock, but I have to say that
1 * bsonWriter.blahblahblah()is much more readable.
- I love that everything after
then:is an assertion, I think it makes it really clear what you expect to happen after you invoke the thing you're testing.