Writing Testable, Maintainable Code

At our company we’ve had a few interns start this summer, so we ran a little workshop with the development team to help teach the team a few things about writing tests before writing other code. In our test driven workshop we did some simple problems in teams. We looked at different problems from Project Euler, and solved them in pairs. We all worked out our own tested solutions to the problems and brought them back to discuss. While looking at these we discussed what was good in each one and what could have been done better. We were pairing more experienced testers with the new guys for this exercise.

I think this went very well actually. These might not be production level challenges to work, but the variety of designs gives great insight into what people were thinking and how they were approaching the problem. I was delighted to see that there were small bits of code very similar between designs, but overall none of them really looked similar. Class names here and there might have been the same, but the implementations of classes with the same name could vary wildly.

After we finished with the exercise, we jumped back into teams to continue working. A while later I got into a discussion with one of our full-time developers and an intern where I ended up explaining one of the main reasons we like testing code. There is the obvious security of having the tests, but there is more than just that. One added bonus that not everyone seems to realize is that writing testable code creates some interesting properties in the code.

Why don’t we take a look at a few of the properties of testable code. I will certainly not cover all of them, but I will try to get enough to demonstrate my point. (these are in no intentional order)

A Few Properties of Testable Code

  • Keeps dependencies to a minimum – Having fewer dependencies means less mocking, faking, and stubbing.
  • Follows Single Responsibility – This keeps things as small pieces which makes for smaller easier to understand and maintain tests.
  • Programming is done against interfaces – This allows you to mock and fake these objects, because your code only knows about the interface.
  • Dependencies are injected into classes – Without doing this unit testing is impossible and only integration tests could be written.
  • The code is well documented through the tests – The tests themselves describe how the code works, and this documentation stays up to date.

The neat part about this is the fact that these properties are present in maintainable code as well as testable. I'll sit here a moment while that sinks in..... Yes, you heard right that in order for code to be testable it must also be maintainable. If you ask someone how to write maintainable code, they might be able to spout off some information about theoretically how to do it. What is much harder is actually implementing solutions which are maintainable. This is why testing is important, because if you want to even be able to write the tests, the code first needs to be written in nice decoupled, well-organized ways.

I don't know about you, but I think it is very cool when you see that the properties of testable code seem to coincide with best practices for writing maintainable code.

Time-Tested Testing Tips - Part 4

Rather than spending the time with needless introduction, I think I’ll just jump right in today. I’ve got a few tips I am going to post today.

Reproduce Bugs Using Unit Tests

Yes, this is another test driven development method. When you’re looking for a bug, it is very common to try to reproduce it. What you might try to do instead of doing this is reproduce the bug manually, fix the bug, and then write a test to prevent it. Well I think that is a pretty bad way of doing things. Reason number one is DRY; don’t repeat yourself. Why did you reproduce it once manually and once automatically? I also wonder how you know you’ve written the test correctly. If it never failed you can’t be certain you’re testing the bug.

If you write this test first you jump right to finding the bug. Seeing the red of the test failing tells you that you’ve found the bug. Having this test will also let you run the buggy code multiple times, which can be useful if you can’t tell right away what is causing the issue. This becomes a faster, easier process. Since you managed to get the failing test, you know that once you’ve fixed it that you’ve at least fixed the bug you tested. Going the other route you were left to assume that you fixed the bug. We all know what happens when we assume… Yes, that is correct, we are sometimes not accurate.

Keep Dependencies to a Minimum

Yes, this is another situation where we are thinking about both the test code and the production code. If you’re finding that the objects you’re testing are requiring a lot of set up you probably need to simplify things. Get some of these tests in place first. These will help you to clamp down the behavior you want to maintain while you refactor. You’ll want to observe plenty of principles while doing this especially the single responsibility principle.

For example if you have a method which takes in a parameter called BankAccount, but all it really needed was the AccountNumber you should alter your method so it takes the AccountNumber instead. This will make testing easier and it will also lessen your dependence. These are great things to do.

Do not pass an object used to get another. Pass only what is required for something to achieve what it needs to. You need to keep things minimal in this sense. It will make testing as well as maintenance much easier.

Make Things Work Before Making Them Right

It is difficult for a lot of us to follow this, because we know so well how bad some types of code are. We try to avoid having ugly code by spending lots of time and effort trying to get things right the first time. Then we aren’t even sure they work.

Plenty of times in the past, I’ve had programming partners who wanted me to write my code better than I did the first time. Sometimes I made the foolish mistake of listening to them. If you’re dealing with some complicated logic you need to write it the ugliest easiest way you can. This will let you get your tests passing. Once your tests are passing you are free! By knowing that the test and the code are working you have freed yourself up to refactor. You don’t have to think quite so hard since you have this safety net in place. Try something. If it does not work, just revert back and try something else. You will quickly find a design that you like and keeps those tests passing.

Sometimes just seeing the working code will lead you to seeing a better design for it. This situation is much better than if you had spent a lot of time coming up with some way of designing it. The design might not even have worked the first time, and then you would really be wasting time.

I hope you’ve enjoyed these tips. Remember that testing takes a lot of practice. You’ll never see any benefits from testing if you don’t keep writing tests.

Time-Tested Testing Tips - Part 3

Context switching is very costly and this same issue can be seen when writing tests, but I would argue most importantly in maintaining and reading tests.

There are three main parts to a test. The first part sets everything up, the second part takes some action, and the third part expects certain results given the the start and the action taken. In this tip I'll be talking about the first part, the setup.

Keep Test-Relevant Details Visible

I've said in the past to treat test code just like any other code. However, there are a few reasons to break from this rule. One is the context switching which will be caused by extracting information which is important to a test. Let me explain what I mean with two examples.

Example Using Helper Method

[Test]
public void CalcInterestRoundsToTenthPennies()
{
    decimal initialMoney = 100.00;
    decimal expectedInterest = 33.333
    InterestCalculator ic = GetTestInterestCalculator();
    
    decimal actualInterest = ic.CalcInterest(initialMoney);
    
    Assert.AreEqual(expectedInterest, actualInterest, 
      "CalcInterest did not round to a tenth of a penny correctly");
}

Example Without Helper Method

[Test]
public void CalcInterestRoundsToTenthPennies()
{
    decimal initialMoney = 100.00;
    decimal interestRate = 0.3333333333;
    decimal expectedInterest = 33.333
    InterestCalculator ic = new InterestCalculator(interestRate);
    
    decimal actualInterest = ic.CalcInterest(initialMoney);
    
    Assert.AreEqual(expectedInterest, actualInterest, 
      "CalcInterest did not round to a tenth of a penny correctly");
}

Notice here that you can tell a lot more about what is going on in the second one, because you know what percentage is being used to calculate. You cannot tell that the first one is accurate since you can't see the percent. I recommend trying to keep the amount of values to a minimum and if you're writing your code well you'll have few enough dependencies that you will not have a problem. These helper methods as you can see make it so you will need to go to another location to see what was initially created. In some instances they are useful, but use them sparingly as they might hide away details.

Do Not Unit Test Third Party Frameworks

Make sure that what you're testing is part of your code and not someone else's. If you're testing code you have no access to you better not be keeping that around as an automated test. You're wasting your time if you're creating automated tests for this code. If you find a bug in the code you can tell someone about it, but you probably can't fix the code. If it is an open source project you're testing then go test the code in their test library and fix issues you find. You will be doing them and yourself a favor. Don't clutter your own test library.

Write Automated Tests Whenever You Are Curious

If I ever wonder how some piece of code or a class works or something, I could go write a simple application and test something out. I certainly do not waste my time though. It tends to be much faster just to write a test to answer my question or learn something new. If I want to try something out I just write a test. If it is an internal piece of code I go look at the existing tests, and if it is not I write a temporary one just to try something out.

Unit tests are quite useful for this sort of thing, and since I have keyboard shortcuts mapped to run them they're very fast. If you really want to test a lot, you need to start using them all the time. They become very easy to come up with and write once you practice them enough.

Time-Tested Testing Tips - Part 2

In the first part of this series of testing tips, I mentioned a couple of tips I believe to be quite useful. I am going to continue this series today by writing about a couple of more ways to write better tests. I also plan to give reasons for testing and describe different benefits of the practice as I go.

Test Driven Development

Yes, I am going to bring this up. Plenty of people have latched onto the idea that testing code helps make code better. A lot of people even believe that it makes the process of writing the code faster. I see a lot of people who are reluctant still to use test driven development. I know I am throwing around a buzzword… or maybe I mean buzzphrase since that’s actually three words.

I don’t plan on doing TDD justice here since I am trying to keep these tips relatively short. I just hope this is enough to inspire people to go read, learn more, and try some test driven development. I am going to give some quick reasons why developers testing their code should write the tests first.

  • You will not want to go back and write it later. Sometimes you are going to go back and write the tests for the code you have already written, but what happens if you decide you’re going to write a lot of code and then go back and add the tests. Now you’re talking about a lot of testing all at once. There is a good chance you’ll cut corners and maybe skip entire aspects of the testing. I know I would be tempted.
  • How do you know what kind of interface your new code needs if you’ve never used it? If you start by trying to use some code that does not exist yet, you’ll be deciding how you would want to use it. Now you’re probably thinking you could do that anyway. If you write code to use some new feature you will know the design is easy to use and work with. You just made it up and used it in the test. If you didn’t write the test you had to guess what interface you will want later.

Organize, Refactor, and Take Care of Your Tests

I recommend you repeat this daily, “My test code is just as important as my production code”. I think this is a very good point to remember when you’re writing tests. All of those principles you apply when writing production code should be followed with test code. DRY, SOLID, YAGNI, etc. are all important even with the testing code.

Obviously duplicating code can make your tests difficult to maintain. What if some business logic changes? If you were repeating yourself you now have the fun task of going through a dozen tests changing each one, but if you had not repeated the same code you might have been able to update one location in the test code. A lot of people are concerned when the line count of a single file gets large and they will refactor it into multiple manageable files. This same policy should apply to test classes. If you’ve ever gone into a test class with way too many classes, you probably know how difficult it can be to maintain.

Tests exist to make development easier, and if they become difficult to maintain then something needs to change. I certainly don’t advocate spending large amounts of time refactoring the tests, but since they are supposed to increase the longevity of the application they must also be maintained.

Adjust Your Style to Your Test Framework

I obviously recommend that everyone use a testing framework. There are plenty of them out there, and they supply a great deal of the tools you’ll need for testing. They come in all languages, flavors, and colors. You might look into these NUnit, MSTest, JUnit, CppUnit. What is important is that you make sure you know how your tools work so you can test best with them.

I will elaborate on a couple of examples and perhaps down the road I’ll give some more specific examples.

Some tools show you a list of test names, and only minor details about why the test failed. For this you usually need to go the a description view or something similar. In these cases it is important to name your tests effectively.

As an example if I have a method called Add and it takes two parameters a and b. I might write a test for that method. If I name my test TestAdd, and that test fails you know something is wrong in that method, but you do not know what failed. If I had instead made a few more specific methods you could glean more information from the test having failed. Some examples of tests I might create are AddTwoZerosShouldBeZero, AddNumberToZeroShouldBeTheNumber, AddPositiveNumbersTest, AddNegativeNumbersTest, etc.

Some parts I would lump together like positive numbers and negative numbers. It is important to handle a couple of different scenarios as well as the edge cases. I could have done negative and natural numbers and that would cover all numbers, but I wanted to make sure the edge case, zero, was handled correctly, so I test it separately.

People can argue back and forth all day long about whether you should have a lot of small tests or group them together, but this is what has worked well for me in the past and I hope it works well for you also.

Choosing A Dependency Injection Pattern

There are a few patterns you can follow when writing code which injects dependencies; constructor, property, and method injection. Which way is the best one to use? I don't really know for certain, but I can talk a little bit about the differences and some of the benefits of each. I am sure this behaves as most situations do with certain ones perform better in certain situations.

Constructor Injection

In this form of injection we pass the dependency into the object using its constructor. Before creating an instance of our object we have to get instances of all of the dependent objects required by the class and pass them in.

I really like this form of injection because it defines up from what is required to use the object. The constructor is saying up front, "these are what I need in order for you to create an instance of me, so if you don't have them bugger off".

public class Car
{
    private ITransmission _trans;
    
    // Constructor Injection
    public car(ITransmission trans)
    {
        _trans = trans;
    }
}

Property Injection

This pattern allows you to set the dependency after the instance of your object has been created. I shy away from this type simply because it concerns me that objects might not have their dependencies set if they're not required by the constructor.

I like using property injection in certain circumstances. I like using them in combination with constructor injection. I use them this way in instances where the dependent object might need to change.

public class Car
{
    // Property Injection
    private ITransmission _trans;
    public ITransmission Transmission
    {
        get { return _trans; }
        set { _trans = value; }
    }
}

Method Injection

If a dependency is really only needed by one method it is nice to put it in the method signature. This lets everyone know the method requires it. If we were using property injection we might have needed to know in advance to set the property. This type allows you to have one less property to set or one less parameter in the constructor.

Also if a method sometimes needs different instances of the dependency this is the way to go.

public class Car
{    
    // Method Injection
    public void Shift(ITransmission trans, ShiftDirection direction)
    {
        // do stuff here
    }
}

Putting Them Together

As I said moments ago, I try to use these techniques collectively. It allows me to leverage the benefits of each. I think as a general rule you should try to tend towards one of them. I've seen people make great use of each of them.

If you can keep your dependencies to a minimum and localized to certain methods then method injection works pretty well. Property injection requires that you be more careful to make sure the dependencies are being set, but can eliminate a lot of clutter which can be found with method and constructor injection. Constructor injection is nice because it documents up front in the method signature what dependencies exist in the class.