An assortment of basic TDD tips

I spent most of last week working strictly TDD-style on a work project. Now all the stuff I do at home is done using TDD, and I sometimes manage to test-drive a feature into a work project, but for a number of reasons I’ve never jumped 100% into test driving stuff for work.

For the first few days I had mixed results. The code and design was probably not significantly better or worse that I would have come up with without TDD. I did end up with a decent suite of tests for the code, which was a nice bonus, and the code took a similar amount of time to non-TDD for me.

The last couple of days were a very different story: I felt the code and design turned out significantly better and was written much faster than I would generally have done sans TDD. The whole process flowed wonderfully.

I thought I’d jot down a couple of rambling points I learned or re-learned during the week, for my own future reference and on the off chance I help someone else experimenting with TDD. These are all pretty basic points, but ones which I sometimes lose sight of while coding in anger :-).

When I’m not sure how to do something, my tests tend to drive the procedure instead of the design

This was something that occurred to me while test-driving some code to generate XML schemas (XSD). It had been a while since I had done anything manual with XSD, so while I knew roughly what what I was trying to do, I was not really sure of the details on how to do it. So I started writing tests about how I thought XSD worked, and responding to the feedback from those tests.

The end result was that my tests built up the procedure of how to generate XSD, rather than a good design for what I was trying to achieve.

While this can be helpful in itself to learn an API or something else that’s a bit new to you, it is not what you really want for production code. So next time I’m in a similar situation I’ll make sure I do a quick spike (possibly test-first if I think it will help) to learn how, before throwing it away and starting to test-drive the production code.

Avoid complex APIs until you really need them

Back to my previous XSD example, we have a few options as of .NET 3.5. First up, XSDs are just strings, so we could work at that level. They are also valid XML documents, so we could use XmlDocument, XDocument, or XmlWriter. Finally, .NET has a built in XmlSchema and related classes specifically for dealing with XSD documents. So which would you chose?

I started with strings, but quickly ran into the problem mentioned in my previous point: I had forgotten many of the details of XSDs that I needed to build up the right string. So I back tracked and jumped straight into XmlSchema, leaning on the API a bit to relearn everything. This introduced a lot of needless complexity into my design to enable it to play nicely with the API.

In retrospect I think I’d have been better off sticking with strings or another basic construct until I really needed a full featured API.

Have a clear Subject Under Test (SUT)

Especially when starting out with testing (any testing, not just TDD), I have occasionally managed to confuse myself setting up the test context – the data and dependencies required for the test. I would sometimes end up testing the test data, rather than exercising the production code.

This is probably (hopefully?) a bit less stupid than it sounds, as sometimes your code will simply delegate to another object, in which case you can test the state (test data that comes back) or the iteraction itself (usings mocks).

It pays to be very concious of exactly what your Subject Under Test (SUT) is. I’ve found myself thinking increasingly in terms of the SUT, its dependencies, and its behaviour to be exercised in the test*. Once clear on these points, it’s time to setup the dependencies to provide a specific testing context and test data, exercise the SUT, then verify that the SUT performs correctly in that context. Using a four-phase test structure can help here.

* I’m getting crazy ideas about single test contexts per class, where the test context becomes the class state. A bit like AMC but less to do with mocking and more to do with structure and easy reproduction of test context across related tests/specifications.

Use test difficulties to drive design

After completing a couple of tests on a class, I found there was a method that seemed like it should be protected, but that I still wanted to have tests around. There are lots of valid approaches to do this: using inheritance (either a test double, or self-shunt [PDF]), InternalsVisibleTo, generated mocks, reflection tricks, or just leaving the method public. But why should we have to go through these hoops just for testability’s sake? Why is something so trivial requiring any complexity or workarounds?

This kind of signal is generally referred to as a "smell" – something that just doesn’t seem right, and it is this kind of feedback that TDD is very good at providing.

In this case the smell prompted me to have another look at the code, and I discovered that the method actually seemed to belong on another object. So I moved the method to the other class, and had the original SUT delegate to it. This meant I could test the now public method on its new class, while keeping proper encapsulation for the original class. And even more importantly, the design was now much cleaner and clearer (IMHO).

So if you come across something that seems a bit messier to test than necessary, it might be your test’s way of telling you there is something amiss with your design. Sometimes you may look at it and decide it is worth a bit of mess, but other times it is a clear cue to refactor.

Use tests to drive through uncertainty

I was test driving a controller class that accepted requests for specific resources. I had tests for a VerifyAccess(Guid id) method which loaded some information about the resource with that ID, then ensured the caller had access. A few tests later and I had another method, GetResource(Guid id), which actually retrieved the resource. Now GetResource should really check the access as well.

//Pseudocode
public void VerifyAccess(Guid id) {
  //Get details about resource with id
  //Check access, throw if access denied
}
public Resource GetResource(Guid id) {
  //Get details about resource with id
  //Check access?
  //return resource
}

So I had the situation where both methods wanted to call each other. As the access verification logic was important, I also wanted to keep it testable in isolation from the code to retrieve the resource. It looked like a simple matter of extracting a private, helper method called from both methods, but I was not sure… there were a few ways I could do it, but the resulting method names and code structure all seemed a bit convoluted and unnatural.

My approach was to write a test that exposed the required behaviour. When doing something that calls GetResource, the attempt should fail if VerifyAccess would also fail. It was then just a matter of getting the test to pass.

My final implementation was much nicer than my initial, uncertain guess. Turns out that GetResource would be better suited as a protected method called GetResourceAndCheckAccess. Callers never actually had to get a resource, they only needed to use one. So the old tests around GetResource became tests around UseResource(Guid id) (I’m changing the scenario from the original problem as don’t want to post work stuff verbatim, so the real names are a lot more natural. Hopefully the main idea is clear though). VerifyAccess also called GetResourceAndCheckAccess. I also ended up with a nice, private helper method, verifyAccessToResource(Resource resource), that took a loaded resource rather than an ID. The intention of the code was now obvious from the names and structure, and the tests acurately specified the required behaviour.

//Psuedocode
public void VerifyAccess(Guid id) {
  GetResourceAndCheckAccess();
}
protected Resource GetResourceAndCheckAccess() {
  //Get details about resource with id
  verifyAccessToResource(resource);
  return resource;  
}
private void verifyAccessToResource(Resource resource) {
  //Check access, throw if access denied.
}
public SomeOutput UseResource(Guid id) {
  Resource resource = GetResourceAndCheckAccess();
  return resource.DoSomething();
}

While it’s obviously pretty simple to come up with a solution without going test-first, once the required behaviour was specified correctly and protected by automated tests, I found it much easier to work towards a usable implementation without having to worry about correctness all the time. Provided the tests stayed green, I knew the implementation satisfied the requirement. For me, this approach lets me focus on incrementally getting it right, without being distracted by the uncertainties caused by having many ways of proceeding to a "final" answer.

Hope this is of some help to someone. It was definitely helpful for me to explain it all in any case, so if you made it this far, thanks for listening! :-)

Comments