Skip to content

Wakaleo Consulting

  Home
I don't unit test my classes E-mail

I don't unit test my classes. I don't even unit-test my methods. You'll be hard-put to find the word "test" in my source code. And I never, ever create a new JUnit Test Case Eclipse.

I prefer to test how my application behaves. And I find it makes a huge difference.

So how does that work? Well, I usually start off with a user story. Or, more precisely, with an acceptance test criteria. But I could have started with detailed specifications - it doesn't really matter. The point is, I'm starting with some requirements - in other words, a description of what my code is supposed to do.

In practice, I will usually try to automate the acceptance criteria. Maybe a high-level test that steps through the whole user story to give a picture of where we are going. Automating the high-level test criteria, even incompletely, is a great idea, as it gives you a set of goal posts, an idea of what you are supposed to achieve for this requirement. It also forces you to think about the requirements in very concrete terms. I'll elaborate on this approach (which is known as Acceptance-Test-Driven-Development, or ATDD) in another article. I won't focus on domain modeling, or domain-driven-development, or high-level architecture workshops, or any other of the numerous technical activities that might end up going between the high-level requirements and the actual coding - all these have their place, but I don't want to discuss them here. In this article, I want to focus on the more low-level tests.

I start off the actual coding by creating a new test class. But I don't call this class AccountTest or GizmoTest, or any other name with the word "Test" in it for that matter. I prefer to name my test after the feature I am trying to implement: WhenAnAccountManagerConsultsTheClientDetails WhenAClientTransfersMoneyBetweenAccounts, or WhenTheGizmoSpinsInAClockwiseDirection. I might need several such test classes to fully explore the feature I'm working on. And I might need to drill down, later on, into more technical details. But the essential approach is this: it's all about providing context.

This is by no means just a formality. Naming the class well forces me to think about the feature I'm trying to implement. And it's harder than it sounds. But it gets me thinking about what the application will do in practice, and is the first step in the detailed design process.

Next, I drill down into the details. What exactly should the code do? I typically express this in the form sentence-like structures, very similar to the acceptance criteria. If I need to, I'll add some extra ones to clarify the requirements further. This is pretty standard TDD-stuff - nothing fancy here. The important thing is to make sure each one contains a context, an action and an expected outcome:

  • When the account manager clicks on the client name the client details page should be displayed
  • When the source account has insufficient funds, the transfer is refused and no money is deducted from the source account
  • When the gizmo reaches 10 RPS, the red flashing light goes on.
  • ...
Sometimes the name of the class acts as a context, so you can get away with just the action and the expected outcome. But there should be an action and an expected outcome expressed somewhere.

Each of these sentences becomes a test. The exact form of the test will depend on your project. For Java projects, JUnit 4 will work fine, as will nUnit for .Net code. For the higher-level tests I am more inclined to use a dedicated BDD framework such as easyb or JBehave. For Groovy and Grails, Spock is a great developer-focused BDD tool. And Ruby developers are very fond of RSpec and Cucumber. But even if you don't want to open a new tool box, you can still take advantage of this approach - just pay attention to your test names!

In JUnit 4 code, for example, the tests might look something like this: public void whenTheUserClicksOnTheClientNameTheDetailsPageShouldAppear(){ ... } or public void whenTheSourceAccountHasInsufficientFundsTheTranferIsNotDone() { ... }

And now that I have a good idea about what I am supposed to be coding, I can start to think about classes and methods. Indeed, how could I start to code, if I don't understand the problem?

With a bit of care, this approach also give you a clean, well-organized, and above all working set of examples covering in detail how the different features that make up the application have been implemented, and how classes used to build the application are meant to be used. The class names and the method names read like a narrative. All you need to do is to organize them into sensible packages (for example, by feature).

So I'm not actually testing my classes, or my methods. I'm testing the behaviour of my application - I just happen to be doing it by exercising classes and method calls. And the same approach applies at any level - I could be implementing web tests using page objects, or writing a low-level technical module. The same principles apply.

This approach is of course not new - it's known as Behaviour-Driven Development, and there are some great tools out there to help you formalize the process. No matter what tool or language you are using, I would recommend 'The Rspec Book', by Dan North and friends, as a good introduction to the general approach.

Tags See All Tags Add New Tag...

Please Enter New Tags Separated By Comma's
  Or Close

Agile  BDD  EasyB  JUnit  TDD 

Trackback(0)
Comments (8)Add Comment
0
It's all about finding the balance
written by Itay Maman, August 31, 2010
High level test describe the system have better coverage - a single test covers large parts of the code. This is a good thing but it has its drawbacks - when a high level test fails it only gives vague information about the location of the bug.
(lower-level) Unit test are the opposite: their coverage is limited, but when they fail they give a pretty good location of the bug.

While I like very much your approach and naming convention, I feel that a good test suite should exhibit a reasonable mix of high- and low- level test.
0
High level tests are good too
written by John Ferguson Smart, August 31, 2010
Agreed. Although this article is mainly about low-to-medium level tests, the same approach also applies for high-level tests, which obviously have their value too. Don't be too taken in however by the apparent "coverage" of high-level tests - they usually provide good coverage in breadth (end-to-end features), but not necessarily in depth (traditional "code coverage") - you need unit tests for that. But you are of course right in saying that unit tests excel at isolating bugs. But I think this is a subtle difference in your use of the term "coverage" :-).
0
... then why not just use BDD?
written by Caligula, September 01, 2010
And avoid impossible-to-read method names?!
0
How about fixing the site first?
written by devent, September 01, 2010
What's wrong with this page? The right menu with "upcoming" and "tag cloud" are all over the blog text. I can't read the text because the right menus are over the text. It's only if I go to any blog post, the home page and other pages looks right. If that quality is what Wakaleo Consulting offers you really don't need unit tests. It's really rare to find such a broken page (i.e. this page is the first one so broken). Here is a screen shot http://www.flickr.com/photos/5...945521311/
0
Formatting fixed
written by John Ferguson Smart, September 01, 2010
Formatting issue related to text-only zooming in Firefox fixed. Thanks for pointing this out.
0
Hear! Hear!
written by Dan Bergh Johnsson, September 01, 2010
I could not agree more. The purpose of code is to have - a purpose, i e a behaviour; and that is what we want to test. Have touched on this subject myself at
http://dearjunior.blogspot.com...-code.html.
Well put!
0
It is not mutually exclusive....
written by Azriel Abramovich, September 17, 2010
Quite a few times a very specific unit test, for a class, saved the day. None of the functional scenarios would have picked it up. It is very important when providing services to other layers of the application, when performing negative tests, when no particular scenario/requirement drives the code.

This is not to say that requirement/acceptance driven testing is not needed. On the contrary - it is.

It is just that it is about balance and thinking what is needed, and when.

Azriel.
0
Unit tests should test behaviour too
written by John Ferguson Smart, September 17, 2010
You are right, Azriel, they are not mutually exclusive. This article is actually about writing unit tests using a BDD (behaviour-based) approach, though of course you also need acceptance tests as well.

Write comment

busy