Build Reusable Testable Commands Part 1

June 21, 2012 — 1 Comment

Commands can be complicated to test and reuse. In a previous post, I demonstrated how to build reusable testable Queries, in this series of posts I will show you how to build commands which can be tested and reused.

This small yet powerful interface can help you build commands which respect SOLID principles like the Single Responsibility Principal (SRP), the Open/Closed Principle (OCP) and the Dependency inversion principle (DIP).

public interface IModelCommand<in TModel>
{
    void Apply(TModel model);
}

Model Commands

The Model Command is very practical in many situations. It allows us to specify the parameters of the command and the Model upon which it will be applied. By passing the Model as a parameter to the Apply method, we are able to instantiate the command once and execute it on as many models as we want. Commands are also very useful, because they help us to concentrate on one thing at a time. By doing so, we create systems that are simpler to maintain and test.

For example, if I want to deactivate all users that have been inactive for a specific number of days, I can apply a command to  a collection of users that was returned from a query.

The following command accepts an IEnumerable of User as a Model and it will set the IsActive to False when a user has not logged in for the amount of time specified in the TimeSpan which is passed in as a constructor argument. Constructor arguments enable us set the command parameters as readonly, which can be very practical in asynchronous and multi-threaded environments.

public class DisableInactiveUsers : IModelCommand<IEnumerable<User>>
{
    private readonly DateTime cutOffDate;

    public DisableInactiveUsers(TimeSpan olderThan)
    {
        cutOffDate = DateTime.Now.Subtract(olderThan);
    }

    public void Apply(IEnumerable<User> model)
    {
        foreach (var user in model.Where(user => user.LastLogin < cutOffDate))
        {
            user.IsActive = false;
        }
    }
}

Setting the command parameters through the constructor allows us to create immutable commands. Preserving this immutability is key when creating commands that are reused or applied to models in a multi-threaded environment. By limiting the side effects to the Model we are effectively saying that by convention, when we apply a command to a Model, the only thing that will changes is the Model instance.

Unit Testing Model Commands

Testing commands can be complicated. The complications comes from the fact that commands are tightly coupled with the system which they affect. In some systems we use Inversion of Control (IoC) frameworks to instantiate or find the models upon which to act. By passing the Model instance to the Apply method, we retain in complete control over it. We can inspect the model’s state before and after each command, enabling us to test our commands.

[TestClass]
public class UserModelCommandTests
{
    const string AllUsersWereNotActive = "All Users were not Active";
    const string OnlyUsersShouldRemainActive = "Only 2 Users should remain active";
    readonly TimeSpan thirtyDays = new TimeSpan(30, 0, 0, 0);

    readonly IEnumerable<User> users = new List<User>()
    {
        new User
            {
                IsActive = true,
                LastLogin = DateTime.Now,
                UserName = "Alexandre"
            },
        new User
            {
                IsActive = true,
                LastLogin = GetPastDate(100),
                UserName = "Hugo"
            },
        new User
            {
                IsActive = true,
                LastLogin = GetPastDate(20),
                UserName = "Emma"
            }
    };

    [TestMethod]
    public void TestDisableInactiveUsers()
    {
        Assert.IsTrue(users.All(u=>u.IsActive),AllUsersWereNotActive);
            
        var disableInactiveUsers = new DisableInactiveUsers(thirtyDays);
        disableInactiveUsers.Apply(users);
            
        Assert.IsTrue(users.Count(u=>u.IsActive) == 2, OnlyUsersShouldRemainActive);
    }

    private static DateTime GetPastDate(int daysAgo)
    {
        return DateTime.Now.Subtract(new TimeSpan(daysAgo, 0, 0, 0));
    }
}

Entity Implementation

public class User
{
    public bool IsActive { get; set; }
    public string UserName { get; set; }
    public DateTime LastLogin { get; set; }
}

References

Trackbacks and Pingbacks:

  1. Links of interest #Week2 | Louis Turmel Blogs - April 30, 2014

    […] Build Reusable Testable Commands Part 1 […]

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

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 )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.