Update

I worked up a video on Typemock. Check it out.

Original Post

Ok, that’s a bad attempt at a humorous pun, but it’s still a valid statement. TDD exploded a few years ago and has gained tremendous following and prestige in the development community. I’ve never actually practiced TDD other than tutorials and more recently I’ve been doing TDD katas. If you haven’t done a TDD kata, I recommend it. I’ve learned about myself that I over think the requirements causing me to spend time on a solution I don’t need (YAGNI principal). I’m working on that though thanks to the TDD katas.

I’m a bit late to the unit testing and TDD game but I have a large need for it at the moment, so when I had the chance to grab a copy of TypeMock’s Isolator at the Inland Empire .NET Users Group, I jumped on it. I’ve been playing around with it and it’s pretty cool to say the least.

Why do we need mocks?

Mocks are needed to isolate a specific method for testing. Methods can have external dependencies that, for whatever reason, are not appropriate to have in the unit test. The best example of this is a method that makes a database call via a repository. Actually having database calls in a unit test is not a great idea. For one thing it’s slow. But really it’s pointless. If the database connection fails for whatever reason, the test will fail. You might think that this is a good thing but it isn’t. Database connectivity is an integration test, not a unit test.

Anyway, the point is to isolate methods. TypeMock Isolator makes this pretty easy. Mocking frameworks have come a long way in the last 2 years and TypeMock is pretty impressive. Let’s take a look at a few examples.

Mocking Bird

When will my attempts at humor end? Sorry. The stage is pretty typical. An order processor needs to get the order and update the sales tax based on the order total. The repository makes a call to authenticate on instantiation. CalculateTax just does some math based on the order’s state and returns the value. Very pointless but this is a blog, not a real world application Winking smile

public class OrderProcessor
{
    public decimal CalculateTax(int orderId)
    {
        OrderRepository rpo = new OrderRepository();
        var ord = rpo.GetOrder(orderId);

        var taxRates = new Dictionary()
        {
            { "CA", 0.50m }, //Bastards!
            { "NV", 0.0685m }
            };

        return ord.OrderTotal * taxRates[ord.address.State];
    }
}

public sealed class Security
{
    public static bool Authenticate()
    {
        throw new NotImplementedException();
    }
}

public class OrderRepository
{
    public OrderRepository()
    {
        if (!Security.Authenticate())
        {
            throw new InvalidOperationException("Could not authenticate user");
        }
    }
    public Order GetOrder(int id)
    {
        throw new NotImplementedException();
    }
}

public class Order
{
    public int OrderId { get; set; }
    public Address address { get; set; }
    public decimal OrderTotal { get; set; }
    public decimal Shipping { get; set; }
    public decimal Tax { get; set; }
}

public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
}

Nothing special. Notice that the repo and security don’t do anything, but throw exceptions instead. This doesn’t matter since we don’t care about those actions anyway. We are only interested in making sure the sales tax is calculated correctly.

Now let’s see the code for our test.

[TestMethod()]
public void OrderProcessor_CalculateSalesTaxForCalifornia()
{
    Order fakeOrder = new Order()
    {
        OrderTotal = 100.00m,
        Shipping = 25.00m,
        OrderId = 10,
        address = new Address()
        {
            State = "CA"
        },
    };

    Isolate.WhenCalled(() => Security.Authenticate()).WillReturn(true);

    var repo = Isolate.Fake.Instance();
    Isolate.Swap.AllInstances().With(repo);
    Isolate.WhenCalled(() => repo.GetOrder(0)).WillReturn(fakeOrder);

    OrderProcessor target = new OrderProcessor();
    int orderId = 10;
    decimal expected = 50.0m;
    decimal actual;
    actual = target.CalculateTax(orderId);
    Assert.AreEqual(expected, actual);
}

First we new up a dummy order populating the needed data. Then we use Isolator to specify that whenever a call to Security.Authenticate() is made, it will intercept it and return true. This way we can always test our method without needing to bother with setting up credentials, etc etc etc. We don’t care about security, it has nothing to do with sales tax!

Then we create a fake repository object using Isolator.Fake. Then, using Isolator.Swap, we tell Isolator that anytime a new instance of OrderRepository is instantiated, we replace it with our fake instance. Then we specify that anytime the GetOrder method is called on the fake repo instance, we will intercept and return our dummy order instance.

Finally, we instantiate a copy of OrderProcessor and make a call to CalculateTax. We assert that the returned value matches what we expect it to be.

Conclusion

Even though a call to the CalculateTax method requires a call to the database and a security check, we were able to isolate the tax calculation logic by mocking up the dependencies. I like the way Isolator works because you don’t have to specifically code your classes for unit testing/IoC/DI in order to pass in the fakes and mock objects.

There is plenty more that TypeMock Isolator will do so make sure you check out their documentation.

Advertisements