TDD and Unit Testing Guidelines

Over the past few months I’ve been watching how we do TDD. Or more importantly how we don’t do TDD. This got me thinking and raised a lot of questions. Why aren’t we achieving TDD, and what are our goals? With this in mind I went back to basics, re-learning unit testing.

What is TDD?

It often comes across that a lot of people think they are doing TDD, when in actual fact they aren’t. Having unit tests doesn’t mean you’re doing TDD. So what is TDD? It’s simple really, it’s development whereby your tests drive the development (it’s all in the name!). If you’re writing code first and then tests later, this isn’t TDD as your tests haven’t driven your development.

Should Developers Do Testing?

Yes. There’s no other answer.

Developers should always be testing their code to ensure what they’re coding works as expected. This can be done using unit tests and functional tests. The role of the QA team should be to confirm the the quality of the code from a scenario perspective. Raising bugs is something, in an ideal world, they should not be doing.

Also developers should help the QA team with testing (but not stories they’ve worked on). This gives them better knowledge of the product from a customer perspective (as opposed from just a code perspective). It also ensures your team is more agile.

What are the benefits?

For me, in this order:

  1. Better Code – TDD is a design pattern for cleanly designing your code. It won’t architect your code on a larger scale, but it will ensure on a smaller level that all your classes are cleanly and correctly designd in a modular way.
  2. Increased Stability – In the long term your code is more stable, and the chances of regression bugs being introduced are far less. As your product evolves you often have to return to old code. Having unit tests allow you to quickly identify if your changes have broken any business rules. Without unit tests, you run the risk of introducing regression bugs. This in itself is expensive as a) the bug has to be found (possibly by a customer!), b) verified / recreated, c) you have to re-visit your code yet again, d) it has to be deployed and tested, e) it has to be released. It’s much more cost effective just to get it right the first time round!
  3. Faster Development – When developing new features it should help reduce the number of bugs found. Finding and fixing bugs is an expensive, time consuming process as it takes up QA time as well as your own. Also by defining your tests early on (before writing any code!) there more chance of you fleshing out any unseen requirements, rather than discovering them late on in the development process (when their impact may be higher).
  4. Identifying Errors – Without unit tests, if something stops working (object not set to an instance of an object!) it takes time to check the stack trace, logs etc… possibly open up the debugger to see what went wrong. With unit tests you should get a broken test which will identify the unit of code that is broken (a method), and in some cases the lines of code within the method that are causing the problem. This is obviously much faster as the offending code has been pointed out for you.
  5. Accountability – if asked how far along you are with your Story, rather than give a general “nearly there”, you can be more specific and say 40% of the tests pass. This isn’t always applicable, but when it is, it’s a handy measurement to know how development is progressing.

False Conceptions

There are many misconceptions of TDD, in many cases because people aren’t actually doing true TDD in the first place. Here are some of the common ones and reasons why they’re wrong:

  • It’s Sloweryou have to write tests, which involves writing more code. Also you have to maintain these tests which take up even more time. This does not mean it’s slower though. In the short term you identify all the test cases, and ensure your code passes all of those test cases. This should reduce the number of bugs found, so you possibly save time by not having to raise, verify, recreate, fix, deploy and test as many bugs. In the long term having good code coverage stops regression bugs being introduced. How many live/regression bugs do you have in your backlog? It’s worth questioning how many of those would be there (and the cost of fixing them) if you had proper test coverage.
  • Achieving 100% Coverage Is Not Cost EffectiveThere’s little benefit to the business achieving 100% coverage, the cost and time could be spent on better things. I can agree with this to a certain point but in truth you should be aiming for 100% coverage, and at least coming very close. Having 60% coverage is very good, but that means there still 40% of the code whereby you could introduce regression bugs without knowing. As mentioned regression bugs can be expensive (and embarrassing) for the business. It’s more than just 15 minutes for a developer to fix the bug. It can affect your customer (potentially losing you business), requires QA time, Dev time, and deployment/release time. And that’s for just one regression bug!
  • Achieving 100% Coverage Is Not Always Possible – If you’re finding it difficult to achieve 100% coverage, then it’s an indication that your code needs refactoring. The design is most likely not modular or clean enough. As mentioned above though, there are obviously some cases when 100% isn’t possible, but you should at least be aiming for 100% and coming very close.

100% Code Coverage

If you’re sturggling to achieve 100% coverage, the first question you should be asking yourself is why?

  • It’s difficult to test/reach some of the code – your code may need to be broken down. The class you’re testing may have too much functionality. Common culprits are private methods (this is a more complex discussion). Typically though private methods should be very small and simple. If they start containing logic you should be testing, or are finding hard to test, then chances are they should be a public method placed in another class.
  • The tests are becoming complex and unmanageable – again this highlights a problem with your code. The second your tests start becoming complex (large setups, and many lines of code) then it’s a sign that your code needs to be broken down further. Don’t worry though, this is the natural flow of TDD. You refactor as you go along, and you only refactor when you need to.

Some Rules To Follow

Here are a few rules I’ve found helpful when writing unit tests:

  • Unit Tests – make sure they are fast, modular, independent (not order dependent), and small.
  • Tests First Then Implementation – Probably the rule that most people break (and as a consequence, they’re not doing TDD). Always write the test first, and then the implementation. That way your tests are driving your development. This goes for anything!!! Even if you’re fixing a bug. If there’s no test, then you shouldn’t be writing any implementation.
  • State Based Tests – No more than 2-3 (3 absolute max) fakes (stubs) per test. Review your code design if you find yourself going over this quota.
  • Behaviour Based Tests – No more than 7 or 8 fakes/calls if possible.
  • Mocks – ie a “Call”. They specify expectation (ie .CallBack/Never/OnlyOnce etc… in Moq) and are used to verify behaviour when testing larger areas of code.
  • Stubs – ie a “Fake”. Stand-in objects that return a pre-defined value (ie .Returns in Moq).
  • Too Many Mocks – If you find you’re using too many, review your code! Too much overhead to run a unit test shows that your code has too many dependencies and your class design needs rethinking. As per the first point, your tests should be small and fast.
  • Test Naming Convention – This is all about readability.
    {method}_{scenario}_{outcome}

    For example:

        public void IsEnterpriseUser_NullUser_ReturnsFalse()
  • File Name Convention – {class name}Tests.cs, ie UserServiceTests.cs. It should also be located within the same namespace, ie Huddle.DataAccess as what we’re testing, so a unit test project will sit under Huddle.DataAccess.UnitTests. The tests should sit within a similar folder structure to the class we’re testing. This is so that you can easily and instinctively find the tests related to a class.
  • Test SetUp – If test “SetUp” is used, anything initialised within it must be used by ALL tests.

        [Setup]
        public void Setup()
        {
            _user = new User(); // This has to be used by ALL tests
        }
  • Arrange, Act, Assert – The three parts of a test, in that order (each separated by a line break if there are multiple lines for each). DO NOT mix them (apart from possibly the Act and Assert should it look more readable. Here’s an example:

        [Test]
        public void SetUserStatus_NotEnterpriseUser_ReturnFalse()
        {
            User user = new User(); // Arrange
            _userService.SetUserStatus(user); // Act
            Assert.AreEqual("Deleted", user.Status); // Assert
        }
  • Multiple Assertions – More than one Assertion can be made in a test if they are related. Make sure they all occur after the Arrange and Act part. No more than 3 or 4.
  • Values – If passing in string/int values, use sensible values that relate to what you are testing. For example a subdomain, “meh” = bad, “www” = good. This makes it more readable/understandable.
  • Factory Methods – Avoid using “new” when creating objects in your tests. Use Factory methods, with descriptive methods (like CreateLockedWorkspace()) to make maintaining tests easier (central place of origin).
  • Private Methods – If you need to test a private method and can’t, then in most cases it should be public (in another class). You need to think about exposing it. Unit Testing is about testing small units of code, if you require a large overhead to reach a certain piece of code then something isn’t designed very well.
  • 100% Coverage – this in most cases should be possible. If you’re struggling to test some code, chances are it’s not written as well as it could be. In true TDD, you shouldn’t have written any implementation if there isn’t a test already written for it.

Further Reading

Jeremy Milller’s blog is a great place to start when reading about unit testing. He has plenty of good articles:

In my next article I’ll cover more about private methods, brittle tests (potentially dangerous mocking and stubbing), and Moq (my framework of choice).