Mark is a graph advocate and field engineer for Neo Technology, the company behind the Neo4j graph database. As a field engineer, Mark helps customers embrace graph data and Neo4j building sophisticated solutions to challenging data problems. When he's not with customers Mark is a developer on Neo4j and writes his experiences of being a graphista on a popular blog at http://markhneedham.com/blog. He tweets at @markhneedham. Mark is a DZone MVB and is not an employee of DZone and has posted 534 posts at DZone. You can read more from them at their website. View Full User Profile

TDD: Combining the when and then steps

11.13.2009
| 3580 views |
  • submit to reddit

I've written before about my favoured approach of writing tests in such a way that they have clear 'Given/When/Then' sections and something which I come across quite frequently is tests where the latter steps have been combined into one method call which takes care of both of these.

An example of this which I came across recently was roughly like this:

@Test
public void shouldCalculatePercentageDifferences() {
	verifyPercentage(50, 100, 100);
	verifyPercentage(100, 100, 0);
	verifyPercentage(100, 50, -50);
}
private void verifyPercentage(int originalValue, int newValue, int expectedValue) {
assertEquals(expectedValue, new PercentageCalculator().calculatePercentage(originalValue, newValue));
}

This code is certainly adhering to the DRY principle although it took us quite a while to work out what the different numbers being passed into 'verifyPercentage' were supposed to represent.

With this type of test I think it makes more sense to have a bit of duplication to make it easier for us to understand the test.

We changed this test to have its assertions inline and make use of the Hamcrest library to do those assertions:

@Test
public void shouldCalculatePercentageDifferences() {
assertThat(new PercentageCalculator().calculatePercentage(50, 100), is(100));
assertThat(new PercentageCalculator().calculatePercentage(100, 100), is(0));
assertThat(new PercentageCalculator().calculatePercentage(100, 50), is(-50));
}

I think we may have also created a field to instantiate 'PercentageCalculator' so that we didn't have to instantiate that three times.

Although we end up writing more code than in the first example I don't think it's a problem because it's now easier to understand and we'll be able to resolve any failures more quickly than we were able to previously.

As Michael Feathers points out during Jay Fields' 'Beta Test' presentation we need to remember why we try and adhere to the DRY principle in the first place.

To paraphrase his comments:

In production code if we don't adhere to the DRY principle then we might make a change to a piece of code and we won't know if there's another place where we need to make a change as well.

In test code the tests always tell us where we need to make changes because the tests will break.

References
Published at DZone with permission of Mark Needham, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Vijay Nathani replied on Sat, 2009/11/14 - 12:38am

Somone may like this design better:
private int calculatePercentage(int originalValue, int newValue) {
	return new PercentageCalculator().calculatePercentage(originalValue, newValue);
}
@Test
public void shouldCalculatePercentageDifferences() {
	assertThat(calculatePercentage(50, 100), is(100));
	assertThat(calculatePercentage(100, 100), is(0));
	assertThat(calculatePercentage(100, 50), is(-50));
}
Vijay

Jonas Olsson replied on Mon, 2009/11/16 - 2:35am

[...] it took us quite a while to work out what the different numbers [...] were supposed to represent.

By just looking at the method signature, and making a quailified guess at the purpose of the class under test, I had no problem picking this up almost immediately. I guess the arguments could be renamed, e.g. to originalCalculationValue, newCalculationValue and expectedPercentage, but that's just clarification on code you should already know about as you're reading its tests ...

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.