How-to write tests for extension methods without Typemock

Ok, I’m not a happy owner of Typemock, hence I can’t use it to mock extension methods.

I will show you two really simple workarrounds for achieving this.

I have some simple extension methods that extends IQueryable. One of the extensions is responsible for checking if a certain username allready is taken.
The extension method

public static bool UsernameIsTaken(this IQueryable<UserAccount> userAccounts, string username)
{
    return
        userAccounts.Where(
            u =>
            u.Username.Equals(username, StringComparison.InvariantCultureIgnoreCase)).Count() > 0;
}

As I’m using the Moq-framework for setting up my fakes (in this case stub, see earlier post) I will get an exception.

The test as I want it (which doesn’t work) looks like this

[TestMethod]
public void Validate_TakenUsername_GivesViolation()
{
    var userAccount = UserAccountFactoryForTests.CreateValidUserAccount();
    var userAccountStoreStub = Fake.Stub.New<IQueryable<UserAccount>>();
    Fake.Stub.Returns(userAccountStoreStub, stub => stub.UsernameIsTaken(userAccount.Username), true);

    var validator = new NewUserAccountValidator(userAccountStoreStub);
    var validationResult = validator.Validate(userAccount);

    Assert.AreEqual(TimeTracking.Domain.Resources.UserAccountValidationMessages.UsernameIsAllreadyTaken, validationResult.GetViolationsByMembername("Username")[0].ErrorMessage);
}

This stub is fairly simple to replace manually. All I need is a generic List of UserAccount that contains an instance of an useraccount that has the same username as the username I’m validating.

Solution 1 – Setup a generic list and pass it as IQueryable

[TestMethod]
public void Validate_TakenUsername_GivesViolation()
{
    var userAccount = UserAccountFactoryForTests.CreateValidUserAccount();
    var userAccountStoreStub = new List<UserAccount>
                                   {
                                       UserAccountFactoryForTests.CreateValidUserAccount()
                                   }.AsQueryable();
    
    var validator = new NewUserAccountValidator(userAccountStoreStub);
    var validationResult = validator.Validate(userAccount);

    Assert.AreEqual(TimeTracking.Domain.Resources.UserAccountValidationMessages.UsernameIsAllreadyTaken, validationResult.GetViolationsByMembername("Username")[0].ErrorMessage);
}

This time I was lucky, since the extension method was on the IQueryable, but what if it was something that is totally custom? Just let the extension method consume something that you can stub. In this case, e.g., a Func (see Daniel Cazzulino’s blog).

Solution 2 – Let the extension method consume something you can stub

public static class UserAccountQueries
{
    public static Func<IQueryable<UserAccount>, string, bool> userNameIsTaken = (userAccounts, username) =>
        userAccounts.Where(
                u =>
                u.Username.Equals(username, StringComparison.InvariantCultureIgnoreCase)).Count() > 0;

    public static bool UsernameIsTaken(this IQueryable<UserAccount> userAccounts, string username)
    {
        return userNameIsTaken(userAccounts, username);
    }
}
[TestMethod]
public void Validate_TakenUsername_GivesViolation()
{
    var userAccount = UserAccountFactoryForTests.CreateValidUserAccount();
    UserAccountQueries.userNameIsTaken = (stub, username) => true;

    var validator = new NewUserAccountValidator(new List<UserAccount>().AsQueryable());
    var validationResult = validator.Validate(userAccount);

    Assert.AreEqual(TimeTracking.Domain.Resources.UserAccountValidationMessages.UsernameIsAllreadyTaken, validationResult.GetViolationsByMembername("Username")[0].ErrorMessage);
}

In my scenario I used solution 1, but in the future I will probably refactor away my code from the extension methods.

//Daniel

One thought on “How-to write tests for extension methods without Typemock

  1. Unfortunately, you can’t always use solution 2 either, as you might not have access to the source code (or even if you do, you don’t want to mess with it, since it would be a fork of the official source code that might not benefit from official updates.)

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 )

Google+ photo

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

Connecting to %s