Brendan Enrick

Daily Software Development

Time-Tested Testing Tips – Part 6

If you haven’t seen my earlier posts on this topic. I’ve mentioned a bunch of tips which should make your testing easier and more effective. If you’re looking for more tips check out these previous posts.

Time-Tested Testing Tips - Part 1

Time-Tested Testing Tips - Part 2

Time-Tested Testing Tips - Part 3

Time-Tested Testing Tips - Part 4

Time-Tested Testing Tips - Part 5

Don’t Repeat Yourself

I am sure a lot of you have heard this before and know that Don’t Repeat Yourself (DRY) means that you should reuse code so that you’re not repeating the same code all of the time. This also applies to testing. Sure we know to extract the logic in our tests, because we know that test code should be treated very well.

I am not talking about the logic in your tests not being repeated. I am saying that you should not test the same thing twice. Try to only test each thing once. This makes identifying the issue a lot easier. This is extremely important for edge cases. Make sure you’re only testing your edge cases in your edge case test.

You should have a test for each edge case and preferably it is the only test using parameters for the edge case. If something else is using it you’re creating difficulties in maintenance as well as tracking down bugs.

If you break a piece of code dealing with an edge case it will be harder to track down since two tests will be failing as a result. If there were only one it would be easier to find and fix the issue. Also there is the simple fact that things change. What happens if later on the desired logic of the program changes and the handling of the edge case changes. Well now you have to change the code in two places instead of just the one. This makes it more difficult to change the logic of your program. repetition == bad

Start Writing Tests For Everything

When you’re going to try some new piece of code how do you go about it? I am sure a lot of people will create a small sample Console Application and try something and have it print out results to the Console. This works very well, but if you’re really trying to pick up testing you should try writing a test instead. If you want to see how some new functionality works give it a shot in a test. Write a test to see what happens. Assert on the results and try to predict the output. This is a fun little way to work with things and you’ll get more experience using your testing framework.

It is important when learning to write tests that you do it as often as possible. Get used to writing them whenever you write code, so when you’re looking at something new you might as well look at it with a test. What better way to “test it out” than using your unit tests to do it.

Have a great day! Keep testing!

Constructors Should Be Simple and Stupid

There are plenty of commonly known and upheld ideas about how software should be written. There are rules and guidelines professed by many developers which recommend following this design principle or that one. Some are more important than others. Some are easier to find and correct than others.

One such rule that I believe is important to follow is that all constructors remain simple and stupid. This is about as advanced as I believe a constructor should even be.

public SomeObject(int someValue, int someOtherValue, 
    ISomeKindOfInterface someKindOfInstance)
{
    _someValue = someValue;
    _someOtherValue = someOtherValue;
    _someKindOfInstance = someKindOfInstance
}

These are merely assignment statements which use the local variables (parameters). We never ever want to call methods in our constructors. This includes calling constructors inside of our constructor. There are very good reasons for this which I will explain in detail.

 

 

Reasoning for keeping constructors simple

It is difficult to test classes which do work in their constructors. When work is done in the constructor it becomes more difficult to manage the object. This is because the mere creation of an instance causes something to happen. This is difficult to handle in unit-testing because you’re trying to keep control of all aspects of the code while testing. Control is required in order to test effectively, because you need to be able to expect certain behavior to exist based on carefully controlled circumstances.

Otherwise dependencies are hidden. When we instantiate variables inside of a constructor we are “taking a dependency” on the object we are creating. By depending on that code we are limiting ourselves greatly because we are not able to inject anything else. We are not coding against an interface we are working with a concrete class that will be difficult to change or remove.

When we go to test it will be difficult to do so because of these dependencies which have been hidden away in our constructor. Classes should be honest with you. They should tell you up front what else they are dependent on. This honesty is given by having constructors which are open and giving information freely by asking for the dependent objects through constructor parameters.

Otherwise we are adding an extra responsibility. The Single Responsibility Principle is another commonly held principle that states than any object should have one responsibility. This is powerful because it means that any object should have only one reason to change. When we add code to the constructor we have given the object the responsibility for knowing how to construct the object graph for this type of object. If the object is complicated to construct then there should be something else with that responsibility.

What to look for

Keep your eye out for any and all method calls. If any code other than a field or property is being called from within a constructor this is bad.

Instantiation of other objects is a sure sign of a dependency. This goes for anywhere not just in constructors. If you have the “new” keyword it means you’re dependent on the object being created.

Any “logic” in the constructor. If there are conditional statements then you probably have something to fix.

More than a handful of lines is probably too many as well, and is a sign that the constructor is doing more than it should.

 

Example bad code

public SomeObject(int someValue)
{
    _myKindOfTown = new Chicago();
 
    if (someValue > 5)
    {
        _myKindOfTown.HandleMyKindOfRazzmatazz();
    }
}

How to solve the problem

Removing the Object Initialization

One of the easiest ways to improve things is to get rid of the object initialization from the constructor. Passing the values for these objects as parameters is how we get around the issue of initialization. This makes it so that we do not do the work in the constructor.

In some cases we know we will not use that object, for example in our tests, so we can pass in a NullObject.

This process is called Dependency Injection, and it allows us to “inject” our dependencies into our objects.

Create a class to handle the construction

If your object has some complicated requirements involved in the construction that need to happen every time the object is created, you should think about creating an object whose sole responsibility is to create these other objects. As we said earlier a complicated initialization task is another responsibility, so we can have an object who owns that responsibility.

These are often Factory classes who know how to build other objects.

Fixed Code

// Object creation method in another class
public SomeObject CreateSomeObject(int someValue)
{
    ITown chicago = new Chicago();
    if (someValue > 5)
    {
        chicago.HandleMyKindOfRazzmatazz();
    }
    return new SomeObject(chicago);
}
 
 
// ctor for SomeObject
public SomeObject(ITown myKindOfTown)
{
    _myKindOfTown = myKindOfTown
}