2D Head with a clock as an eyeball.
 Wednesday, October 01, 2008

Is TDD over the top? Surely can I write my tests 'alongside' my code?

Short answer: No.

Long answer:

To borrow Michael Feathers, author of "Working effectively with Legacy Code" the definition of legacy code is code without tests. By writing a function without a test first, you've just banged out a whole bunch of legacy code. It might not feel like it, but you could have just wasted your precious time.

OK, so lets pretend we have some of this legacy code in front of us. We begin writing some tests to cover it. What you'll find is that many programmers do one of a few things:

  • Coder A will write four or five tests for every method and generally not have to change their implementation much, if at all.
  • Coder B writes less tests overall, but in order to get it there they had to change their code due to recognising some unnecessary coupling or logic.

The next few examples are people who would have used TDD or no tests at all...

  • Coder C used TDD but strangely the end result looks like Coder A's code.
  • Coder D thinks unit testing is a waste of time, and has already deployed to production by now and is currently smugly sipping on Kool Aid.
  • Coder E only wrote a few tests, each test concerned with what the code should do, not what the code currently does, or how it goes about doing it.

Now that we have brutally put everybody in a box with rash generalizations and assumptions, lets come up with a way to throw a cat amongst the pigeons. Generally the best way to do this to a developer, is to ask them to add a new feature to their code.

  • Coder A when finished, finds that all the old tests wont even compile let alone pass because the new requirements severely altered the required architecture. Time is wasted rewriting all the tests to facilitate the new architectural requirements.
  • Coder B finds that only half of the old tests build or pass after implementing the new code. Because they removed some coupling it was easy enough to add, but they wasted time on some old tests that were obsolesced by the new architecture.
  • Coder C decides that the existing test suite isn't going to facilitate the new requirements. After they rewrite most of their tests they rewrite the code to satisfy them. Its like much Coder A's experience just in reverse.
  • Coder D deploys the new code to production or QA, only to find that the new changes stomped all over old functionality. What do you mean it 'doesn't work'? That's impossible! Time for a week long debugging session.
  • Coder E on the other hand writes one more test to the test suite and adds as little code as necessary to make it pass, then brutally refactors anything possible. None of the tests needed rewriting in the process. All user requirements are met and the project is delivered on time and on budget. If your lucky that is...

What some people fail to understand, is that using TDD doesn't prevent you from writing bad code. On the contrary, it can encourage you to over specify things. When you over specify, you increase the places in your test suite that depend on how your implementation is structured. The very same thing happens when you write a function, and then a test or write tests 'alongside' your code. This is the mistake that Coder C makes, taking the bull by the tail, instead of the horns.

Coder E has no idea that he/she is in part practicing BDD. Its a natural progression from blindly banging out tests to cover every feasible outcome. Eventually you get a feel for tests that give you maximum 'bang for buck'. These tests provide maximum (quality) coverage for the least effort. In other words, we aren't bothering with a test per function. We are writing one test per desired behaviour.

OK so every now and again, you head down the wrong track and tear up a few behavioural tests. Usually that's because you misunderstood the requirements, not how to code it, and that's where the value lies. If you cant write a behavioural test, then you haven't understood the problem fully. If you don't understand the users problem, why are you coding?

That's why initially, its tempting to TDD blindly, you can ignore certain aspects of the users problem and just simply code until you think you got it right. Of course there are a few other aspects to BDD that I'm sure you're either aware of, or hopefully discover after reading this, but its important to remember:

BDD isn't something new and funky fresh. Its not the latest soda pop fad. At its heart its simply TDD Done Well.


Filed under:  | 
 Wednesday, May 14, 2008

Recently I discovered the xUnit.NET framework for unit testing, and it is a very nice minimalist framework for TDD. I especially like writing tests for F# code using it. It allows me to write very short test suites against any CLR library, in most .NET languages. For example:

#light

open Xunit
open NT.Model

let sut = new Person("Jim")

[<Fact>]
let jim_is_a_geek() = Assert.True(sut.IsAGeek)

The Person class could be written in C# or VB and used here. Also note that the ultimate class name of this test is inferred by the filename. I could add explicit module and namespace information if necessary.

In something like MbUnit / NUnit / Gallio I have to use the following OO style syntax...

#light
namespace NT.Model.Specs
module PersonSpecs

open MbUnit 
open NT.Model

[<TestFixture>]
type jims_behaviours() =
  let sut = new Person("Jim")
  [<Test>]
  member j.is_a_geek() = Assert.True(sut.IsAGeek)

Note that the namespace and module must be declared in order for the gallio runner to discover the tests. I think this might be a bug...

However, regardless of the framework you use, F# also lends itself well to setting up for customized approaches to testing. I feel this has some potential for BDD testing in F#. Take the following example:

#light open Xunit let MustEqual expected actual = Assert.Equal(expected, actual) [<Fact>]let a_fluent_assertion() = 0 |> MustEqual 0

 

Taking this one step further we can take advantage of operator overloading to provide extremely terse tests. Note that I don't actually advocate doing this, only pointing out that its possible:

#light open Xunit let (&=) expected actual = Assert.Equal(expected, actual) let (&!=) notExpected actual = Assert.NotEqual(notExpected, actual) [<Fact>] let an_obscure_assertion() = 0 &!= 1

Based on these samples, I think it is clear that without much effort, a fluent specification interface could be arrived at, perhaps even a domain specific one at that.

F# record types are readily testable also:

#light

open Xunit

type Person =
  { name : string;
    age  : int;
  }

let Jim = { name="Jim"; age=28 }
let Fred = { name="Fred"; age=52 }

let IsNot expected actual = Assert.NotEqual(expected, actual)

[<Fact>]
let jim_is_not_fred() = Jim |> IsNot Fred

Sometimes, the assertion framework isn't needed, but having a test runner is still obviously handy. For the next example I setup a discriminated union and then use pattern matching to determine the outcome of the test simply using the built in failwith keyword:

#light

open Xunit

type Animal =
  | Dog
  | Cat

type Person =
  { name : string;
    age : int;
    pet : Animal; }

let Jim = { name="Jim"; age=28; pet=Cat; }

[<Fact>]
let Jim_cannot_own_cats() = 
  match Jim.pet with
    | Cat -> failwith("Jim owns a cat!")
    | _ -> ()

A snippet from the xUnit console runner output for this failing test:

Tests failed:

1) Jims_specifications.jim_cannot_own_cats : Microsoft.FSharp.Core.FailureException : Jim owns a cat!

On that...hopefully soon tools like Gallio will provide us with some F# support in Visual Studio for the Resharper test runner or even MSTest. At current support for xUnit testing with Gallio in F# projects appears to be a non starter. Alternatively, latest SharpDevelop betas have inbuilt F# and NUnit support which I find to be quite good.

In the meantime we can go all 'old school' on VS 2008 to provide some ease of use.

In the following screen shot I've added the xUnit.NET console runner to external tools by selecting from the menu, Tools, External Tools...

externalTools

Next I like to map this to a keyboard chord, say Ctrl+R,Ctrl+X:

MappingToKeyBoard 

So that now after building, I can run my entire test suite using my keyboard shortcut to yield a testing experience that is 'good enough' for TDD/BDD development in F#, well, that's my opinion of course :)

 testResult


Filed under:  |  |  | 
 Monday, July 23, 2007

I've been struggling with the concept of DI & IoC for a few weeks now. Its not because I don't understand the concept, or the benefits, I struggle with the implementation details. I'm one of those horrible visual learners who needs to see code  before the penny even gets close to dropping.

I found my holy grail of information today, and it was lurking right underneath my nose. I've only recently subscribed to Jeremy Miller's blog and the latter chapters in his series on building your own Composite Application Block seemed a little 'pie in the sky' for me. Ooo boy, was I wrong.

I started at part one this afternoon, and have been reading page after page of excellent content for the DI newbie.

So if you're in the same boat as me, struggling with the question; "How do I create a rich WinForm app, that is comprehensively unit tested, without sacrificing furry animals to the dark gods of the Enterprise Library?" then treat yourself to Jeremy Miller's series on building your own lightweight CAB.


Filed under:  |  |  | 
 Friday, June 08, 2007

Came across this handy technique for testing that methods are raising the correct events. I hope its of use.

[TestMethod]
public void RaisesMyEventCorrectly()
{
  bool lRaised;

  MyObject obj = new MyObject;
  obj.MyEvent += delegate { lRaised = true; };
  MyObjectWithEvents.Method();

  Assert.IsTrue(lRaised, "MyEvent did not fire as expected.");
}


Filed under:  |  |  | 
© Copyright 2008 Jim Burger