It’s ok to wet yourself every once in awhile
Dan North, the veritable progenitor of behavior driven development (or BDD), recently blogged about unnecessary DRYness (meaning don’t repeat yourself) with respect to clarity of intent when it comes to testing (in generic terms of the word). Essentially, in the case of a JUnit test, for example, by utilizing a setUp method and possibly other helper methods, the test itself becomes somewhat cluttered– one must jump around the code to truly understand the intention of the test in the first place. 
In fact, Dan says it quite nicely– tests (or stories, baby) are:
“examples [that] tell a story about what the code does… [and] clarity of intent is found in the quality of the narrative, not necessarily in minimising duplication.”
Thus, over utilizing the DRY principle can be ineffective when it comes to testing; indeed, he makes a great point! If tests or stories are intended to serve as “executable documention” doesn’t it make sense to make them as easy to read and understand as possible?
As such, because it’s my bag, I took the liberty of pondering the depth of favored term DRY and decided that when it comes to clearly expressing intent with respect to stories (as in the case of easyb) or tests, it often pays to be WET– that is, wholly express your tactics, baby.
You see, applying WET to, say, an easyb story then yields perhaps more text, but it leaves no room for misinterpretation, man. For example, imagine a story regarding discounts (this is my touchstone example that is in danger of itself becoming dry– no pun intended either, man). The high level story is such that VIP customers receive varying discounts depending on the amount of money a particular order has– you could even require a minimum number of items too (if you were attempting to move inventory). Thus, one scenario could be:
scenario "VIP customer with 3 items, over $30 receiving 10% discount", {
given "a VIP customer"
and "given they have at least 3 items totaling over $30"
when "they proceed to checkout"
then "they should receive a 10% discount"
}
Of course, from here, stakeholders, through a collaborative effort, realize more scenarios are possible– for instance, the VIP customer has 3 items totaling over $100– thus, a higher discount is applied. A first stab of staying WET (that is, wholly expressing your tactics, man) yields this scenario:
scenario "VIP customer with 3 items, over $100 receiving 15% discount", {
given "a VIP customer"
and "given they have at least 3 items totaling over $100"
when "they proceed to checkout"
then "they should receive a 15% discount"
}
Reading these scenarios (which are executable, by the way!) leaves no room for missing the boat– the tactics involved are wholly expressed!!– reading them is somewhat effortless from the standpoint that you don’t necessarily need to jump around the file to gain a clear understanding of context.
If you prefer staying DRY, the story can become more concise–
before_each "a VIP customer with 3 items is assumed", {
given "a VIP customer"
and "given they have at least 3 items"
}
scenario "VIP customer with 3 items, over $30 receiving 10% discount", {
given "the 3 items total at least $30"
when "they proceed to checkout"
then "they should receive a 10% discount"
}
scenario "VIP customer with 3 items, over $100 receiving 15% discount", {
given "the 3 items total at least $100"
when "they proceed to checkout"
then "they should receive a 15% discount"
}
Both stories convey intent– it is just that the WET one is more clearly expressed– in the DRY example, as more and more scenarios are added (and they surely will), one needs to jump to the top to gain an understanding of the underlying assumption (that is, a VIP customer with 3 items in their shopping cart).
DRY is fundamentally a sound principle– I’m not here to deny that; however, like all good things, it can be overused without regard for a particular situation. In fact, Johann Wolfgang von Goethe (of Faust fame, baby) is quoted thus:
“The phrases that men hear or repeat continually, end by becoming convictions and ossify the organs of intelligence.”
Because it is everyone’s bag, baby, think through DRYness from time to time and realize that in some cases, it is perfectly acceptable to WET yourself (I mean, to practice WET). Can you dig it, man?
| Related odds and ends | ||
|---|---|---|
9 comments Wednesday 02 Jul 2008 | Developer Testing, Software Development
9 Responses to “It’s ok to wet yourself every once in awhile”
Gerard Meszaros in XUnit Test Patterns – Refactoring Test Code:
The DRY principle—”Don’t repeat yourself”—of the Pragmatic Programmers (http://www.pragmaticprogrammer.com) should be applied to test code in the same way it is applied to production code. There is, however, a counterforce at play. Because the tests should Communicate Intent, it is best to keep the core test logic in each Test Method so it can be seen in one place. Nevertheless, this idea doesn’t preclude moving a lot of supporting code into Test Utility Methods, where it needs to be modified in only one place if it is affected by a change in the SUT.
I’m not sure I like the word “tactics”. It sounds like it may refer to implementation details.
How about WET = wholly express the tale?
I can see how this could be a benefit in easyB. – Anything that can eliminate ambiguity from the BA’s writing easyB stories is a good thing.
However if one of your statements is wrong you still have to go and change all occurrences – are you going to find them all?
EasyB is terrifically easy to learn and it isn’t difficult to teach someone about set-ups and tear downs.
Maybe an EasyB scenario ‘constant’ or ‘set’ could be created so a well named variable could be used rather than a set up so a customer/BA can still understand and you don’t abuse the DRY principle?
I don’t even know which language the above is written in, so please bear my made-up syntax:
{ given “a VIP customer”
and “given they have at least 3 items”
and “the 3 items total at least $x”
x = 30 => scenario “VIP customer with 3 items, over $30 receiving 10% discount” { then “they should receive a 10% discount” }
x = 100 => scenario “VIP customer with 3 items, over $100 receiving 15% discount” { then “they should receive a 10% discount” } }
I was really hoping you had a good use case for avoiding DRY – I’ve been wondering whether there are any.
Incidentally, I’d rather write the code this way:
customers filter (customer => customer.isVip && customer.items.size == 3) all (customer => HashMap(100 -> 15, 30 -> 10).highestLessThan(customer.items.price) == customer.items.discount)
Clint- Yeah, I think you are right– tale is good. WET = Wholly express the tale is my bag, baby.
Rich- you know, you are on to something with a well named constant– that is a feature that we’ve played with and it has come up before. Point well taken, friend!
Ricky- your code:
customers filter (customer => customer.isVip && customer.items.size == 3) all (customer => HashMap(100 -> 15, 30 -> 10).highestLessThan(customer.items.price) == customer.items.discount)is slick, baby; however, it isn’t very friendly to non-geeks (which may or may not be a concern!!). Thanks for sharing!I could probably come up with more accessible syntax than that, but I’d need to try it out on some non-geeks. Sadly, my girlfriend already understands Scheme syntax (took about 2 minutes), so that’s her out. And most other people I know can already use Excel, so they could cope with the above syntax given an explanation. Damn.
[...] style of exception verification also happens to be mine; that is, utilize a try/catch phrase, which wholly expresses the test (WET) resulting in a clearer picture of expected (or non expected [...]