Sunday, June 17, 2007

A rose by any other name

Using English to describe a test is quite a challenge. It is demonstrated in the various styles present in the community. My naming conventions, much like my development style, change constantly.

When I first began TDD, my names looked something like this:
public void MethodATest()

Look familiar? What does this test? Well, it gives me a new method. It tells me I have to test all the functions of this method in one test. It does not give business value and does not give any clues when it fails.

This methodology did not last long -- my tests were too large, too complex and tested too many things. When I had a failing test, I had some idea what was wrong because I remembered the method, but my tests did not evolve with my code.

Eventually, my next naming convention looked a little more like this:
public void MethodADoesThis()
or
public void MethodADoesThisWithThat()
or
public void MethodADoesThisWithThatWhenThisIsTrue()
(sometimes this was: public void WhenThisIsTrueMethodADoesThisWithThat())

Although this naming was more descriptive, it was slightly ambiguious, drove too much design in a single test and was not easily read.

The anatomy of a test is basic, there is a setup of preconditions (context), execution of the code (call method), and then verification process (Assert or Verify).

What if our name told us what to put in each part. Would you know how to write it? Would you know what its testing?

Back in February, Aaron made a post about loosly coupled mocks. His naming convention is great. Its simple, has clear rules and forces you to think about what you are testing. It not only follows the anatomy of the test, but puts you in your test before you write it.

What it looks like:
public void Method_Context_Expectation()

So, for the simple hello world test:
public void GreetTheWorld_Always_CallsConsoleWriteLine

Always is a weird context right? Well, its true and writing it helps understand what you are designing.

Now, what if we want to add a condition to calling GreetTheWorld?

Say we only greet the world, if the world exists:
public void GreetTheWorld_WorldExists_CallsConsoleWriteLine
public void GreetTheWorld_WorldDoesNotExist_DoesNotCallConsoleWriteLine

This naming convention also helps us understand when to use SetupResult instead of expectations. In the methods above, we are using the world as context, and the console as verification so we would say SetupResult.For(theWorld.Exists) instead of expecting it -- we don't want our test to fail from context, only when our expectations are not met.

Another nice thing about this convention is detecting smells in my design. If I have 10 methods for GreetTheWorld, because there are many contexts or expectations, I'd know GreetTheWorld had too many responsibilities.

Who would have thought a simple naming convention could have helped so much, thanks Aaron -- or rather the unnamed person Aaron stole this from :P

2 comments:

Sean Chambers said...

it's interesting I found this post. I have recently been struggling with test naming conventions. It's a practice that I still struggle with.

The only conventions I have solid right now are making the test sound like a sentance. I find these flow much better. I put in underscores to signify between context and result.

i.e. WillThrowSomeException_IfSomeDataIsInvalid()

or

CanComplete_SomeCondition

Other than that, I haven't really gone deeper than that. I am a sole developer so ATM, it only benefits myself, but when the time comes to hire more developers I am hoping to make it easy for them to get started quickly.

I do like your ideas you have expressed here, especially because it exposes violations of SRP. nice post!

Sean

TexicanJoe said...

Wendy, I was wondering if you could give me some feedback. I am working on a new BDD framework similar to rbehave. Seeing that you are pretty proficient with TDD I would greatly appreciate your feedback.

http://www.lostechies.com/blogs/joe_ocampo/archive/2007/07/15/more-bdd-xbehave-madness.aspx


Thank you,
Joe Ocampo
www.agilejoe.com