Unit Testing 101: Inversion Of Control

Introduction

Inversion Of Control is one of the most common and widely used techniques for handling class dependencies in software development and could easily be the most important practice in unit testing. Basically, it determines if your code is unit-testable or not. Not just that, but it can also help improve significantly your overall software structure and design. But what is it all about? It is really that important? Hopefully we’ll clear those out on the following lines.

Identifying Class Dependencies

As we mentioned before,  Inversion Of Control is a technique used to handle class dependencies effectively; but, What exactly is a dependency? In real life, for instance, a car needs an engine in order to function; without it, it probably won’t work at all. In programming it is the same thing; when a class needs another one in order to function properly, it has a dependency on it. This is called a class dependency or coupling.

Let’s look at the following code example:

public class UserManager
{
    private Md5PasswordHasher passwordHasher;

    public UserManager()
    {
        this.passwordHasher = new Md5PasswordHasher();
    }

    public void ResetPassword(string userName, string password)
    {
        // Get the user from the database
        User user = DataContext.Users.GetByName(userName);

        string hashedPassword = this.passwordHasher.Hash(password);

        // Set the user new password
        user.Password = hashedPassword;

        // Save the user back to the database.
        DataContext.Users.Update(user);
        DataContext.Commit();
    }

    // More methods...
}

public class Md5PasswordHasher
{
    public string Hash(string plainTextPassword)
    {
        // Hash password using an encryption algorithm...
    }
}

The previous code describes two classes, UserManager and PasswordHasher. We can see how UserManager class initializes a new instance of the PasswordHasher class on its constructor and keeps it as a class-level variable so all methods in the class can use it (line 3). The method we are going to focus on is the ResetPassword method. As you might have already noticed, the line 15 is highlighted. This line makes use of the PasswordHasher instance, hence, marking a strong class dependency between UserManager and PasswordHasher.

Don’t Call Us, We’ll Call You

When a class creates instances of its dependencies, it knows what implementation of that dependency is using and probably how it works. The class is the one controlling its own behavior. By using inversion of control, anyone using that class can specify the concrete implementation of each of the dependencies used by it; this time the class user is the one partially controlling the class behavior (or how it behaves on the parts where it uses those provided dependencies).

Anyways, all of this is quite confusing. Let’s look at an example:

public class UserManager
{
    private IPasswordHasher passwordHasher;

    public UserManager(IPasswordHasher passwordHasher)
    {
        this.passwordHasher = passwordHasher;
    }

    public void ResetPassword(string userName, string password)
    {
        // Get the user from the database
        User user = DataContext.Users.GetByName(userName);

        string hashedPassword = this.passwordHasher.Hash(password);

        // Set the user new password
        user.Password = hashedPassword;

        // Save the user back to the database.
        DataContext.Users.Update(user);
        DataContext.Commit();
    }

    // More methods...
}

public interface IPasswordHasher
{
    string Hash(string plainTextPassword);
}

public class Md5PasswordHasher : IPasswordHasher
{
    public string Hash(string plainTextPassword)
    {
        // Hash password using an encryption algorithm...
    }
}

Inversion of Control is usually implemented by applying a design pattern called the Strategy Pattern (as defined in The Gang Of Four book). This pattern consists on abstracting concrete component and algorithm implementations from the rest of the classes by exposing only an interface they can use; thus making implementations interchangeable at runtime and encapsulate how these implementations work since any class using them should not care about how they work.

The Strategy Pattern

So, in order to achieve this, we need to sort some things out:

  • Abstract an interface from the Md5PasswordHasher class, IPasswordHasher; so anyone can write custom implementations of password hashers (line 28-31).
  • Mark the Md5PasswordHasherclass as an implementation of the IPasswordHasher interface (line 33).
  • Change the type of the password hasher used by UserManager to IPasswordHasher (line 3).
  • Add a new constructor parameter of type IPasswordHasher interface (line 5), which is the instance the UserManager class will use to hash its passwords. This way we delegate the creation of dependencies to the user of the class and allows the user to provide any implementation it wants, allowing it to control how the password is going to be hashed.

This is the very essence of inversion of control: Minimize class coupling. The user of the UserManager class has now control over how passwords are hashed. Password hashing control has been inverted from the class to the user. Here is an example on how we can specify the only dependency of the UserManager class:

IPasswordHasher md5PasswordHasher = new Md5PasswordHasher();
UserManager userManager = new UserManager(md5PasswordHasher);

userManager.ResetPassword("luis.aguilar", "12345");

So, Why is this useful? Well, we can go crazy and create our own hasher implementation to be used by the UserManager class:

// Plain text password hasher:
public class PlainTextPasswordHasher : IPasswordHasher
{
    public string Hash(string plainTextPassword)
    {
        // Let's disable password hashing by returning
        // the plain text password.
        return plainTextPassword;
    }
}

// Usage:
IPasswordHasher plainTextPasswordHasher = new PlainTextPasswordHasher();
UserManager userManager = new UserManager(plainTextPasswordHasher);

// Resulting password will be: 12345.
userManager.ResetPassword("luis.aguilar", "12345");

Conclusion

So, this concludes our article on Inversion of Control. Hopefully with a little more practice, you will be able to start applying this to your code. Of course, the  biggest benefit of this technique is related to unit testing. So, What does it has to do with unit testing? Well, we’re going to see this when we get into type mocking. So, stay tuned! ;)

Further Reading

About these ads

6 thoughts on “Unit Testing 101: Inversion Of Control

  1. You minimize coupling, but you add structural complexity to your code, making it more difficult to understand the piece of code. Therefore I would recommend yo use the Strategy pattern only where you need it. The some holds for IoC and depdency injection.

    • Hi, thanks for reading and taking the time to drop comments! You are right. What you are mentioning is an accidental complexity of the Strategy Pattern. However, the pattern is all about decoupling and encapsulation. You shouldn’t need to know more about the abstracted implementations than its operations which are defined on the interface. This and a solid interface documentation should give you enough detail on what’s going on in the code, thus making it readable enough.

  2. If I only need the interchangeable dependent class for unit testing, I normally add a default to the constructor parameter:
    public UserManager(IPasswordHasher passwordHasher = new Md5PasswordHasher()) …
    This keeps the complexity low for people who just want to use the class without customization.

    • Thanks for your comments. Sure, that is one approach. However, a downside I see in it is that we would be creating a tight coupling between the UserManager class (context) and a specific concrete implementation of IPasswordHasher (strategy). I see this as a downside since the intention of the Strategy Pattern is to reduce class coupling and abstract the context from strategy implementation.

      • It also means that when that concrete class is removed the the class will not compile. Your factory/Service Locator/IoC Container should be responsible for the defaults.

      • That is an excellent point, Bill. We would completely violate the interchangeability purpose of the Strategy Pattern since we can’t remove or change the concrete class without affecting the default behavior of the context class.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s