- | rssFeed | My book on MSBuild and Team Build | Archives and Categories Friday, March 26, 2010

MOQ: Comparing hard coded mocks to moq mocks

I'm working on an application right now which uses some mock classes which were "hand written" instead of using a mock framework. I haven't had much experience with using mock frameworks, but I've wanted to learn more about them. There are many mock frameworks out there, I chose to use moq because the test cases for ASP.NET MVC use moq. What I wanted to do was replace the mock class that I created with a moq class. If you have ever created mock classes before then you probably know that they are pretty annoying to maintain. What I mean is that if you add a method/property to the interface which the mock class implements then you have to update the mock just to get it to build. This is annoying and kinda makes people not to use the mock which then ends up in ignoring/commenting out test cases. To avoid all of this most mock frameworks, including moq, just require you to specify behavior for methods/properties which you are ready to exercise in your test cases. Now I’d like to show you the mock class that I had. This interface that it is mocking is used to abstract the data store.

internal class MockModelContext :IModelContext
{
    public User CreateUser(User user)
    {
        // if id is not set, set it and just return it
        if (user.Id <= 0)
        {
            user.Id = 1;
        }

        return user;
    }

    public User GetUserByEmail(string email)
    {
        if (string.IsNullOrEmpty(email)) { throw new ArgumentNullException("email"); }

        User user = CreateDummyUser();
        user.Email = email;

        return user;
    }

    public User GetUser(Expression<Func<User, bool>> filter)
    {
        return CreateDummyUser();
    }

    public IList<Task> GetRecentTasks()
    {
        IList<Task> tasks = new List<Task>()
        {
            new Task()
            {
                CreatedDate = new DateTime(2010,1,1,1,1,1),
                CreatorId = 1,
                //Description ="Description 01 here",
                Headline="Headline 01 here",
                Id = 1,
                LastEditedDate= new DateTime(2010,1,1,1,1,1),
                LastEditOwnerId=1,
                Name = "Name here",
                NumViews = 3,
                Script = @"<ScriptHere>script</ScriptHere>",
                // TODO: Tags
                // TODO: TaskComments
                User = new User()
                {
                    Email ="one@hotmail.com",
                    FirstName="First",
                    Id=2,
                    LastName="Last",
                    MiddleName="Middle",
                }
            },
            new Task()
            {
                CreatedDate = new DateTime(2010,1,1,1,1,1),
                CreatorId = 1,
                //Description ="Description 02 here",
                Headline="Headline 02 here",
                Id = 1,
                LastEditedDate= new DateTime(2010,1,1,1,1,1),
                LastEditOwnerId=1,
                Name = "Name here",
                NumViews = 3,
                Script = @"<ScriptHere>script2</ScriptHere>",
                // TODO: Tags
                // TODO: TaskComments
                User = new User()
                {
                    Email ="one@hotmail.com",
                    FirstName="First",
                    Id=2,
                    LastName="Last",
                    MiddleName="Middle",
                }
            }
        };

        return tasks;
    }
    protected internal User CreateDummyUser()
    {
        User user = new User()
        {
            Email = "email",
            FirstName = "First",
            LastName = "Last",
            MiddleName = "Middle"
        };

        return user;
    }
    public User GetUserByOpenIdUrl(string openIdUrl)
    {
        throw new NotImplementedException();
    }
    public IList<Task> GetRecentTasks(IEnumerable<string> includeList)
    {
        throw new NotImplementedException();
    }
    public Task AddTask(Task task)
    {
        throw new NotImplementedException();
    }
    public User GetUserById(long id)
    {
        User user = this.CreateDummyUser();
        user.Id = id;

        return user;
    }
    public User SaveUser(User user)
    {
        throw new NotImplementedException();
    }
    public Task GetTaskById(long id)
    {
        throw new NotImplementedException();
    }
}

Take notice of the methods which just throw a NotImplementedException, these are methods that I just added to the interface and haven’t yet written test cases for. (Yeah I know I’m not following true TDD, but I never claimed to be either). Now you can compare that to these methods which use moq to create the mock.

private Mock<IModelContext> CreateMockModelContext()
{
    var context = new Mock<IModelContext>();

    context.Setup(c => c.CreateUser(It.IsAny<User>()))
        .Returns<User>(user =>
        {
            if (user.Id <= 0)
            {
                user.Id = 1;
            }
            return user;
        });

    context.Setup(c => c.GetUserByEmail(It.IsAny<string>()))
        .Returns<string>(email =>
            {
                if (email == null) { throw new ArgumentNullException("email"); }

                User user = this.CreateDummyUser();
                user.Email = email;

                return user;
            }); ;

    context.Setup(c => c.GetUserById(It.IsAny<long>()))
        .Returns<long>(id => 
        {
            User user = new User();
            user.Id = id;
            return user;
        });

    context.Setup(c => c.GetRecentTasks())
        .Returns(() =>
        {
            IList<Task> tasks = new List<Task>()
            {
                new Task()
                {
                    CreatedDate = new DateTime(2010,1,1,1,1,1),
                    CreatorId = 1,
                    //Description ="Description 01 here",
                    Headline="Headline 01 here",
                    Id = 1,
                    LastEditedDate= new DateTime(2010,1,1,1,1,1),
                    LastEditOwnerId=1,
                    Name = "Name here",
                    NumViews = 3,
                    Script = @"<ScriptHere>script</ScriptHere>",
                    // TODO: Tags
                    // TODO: TaskComments
                    User = new User()
                    {
                        Email ="one@hotmail.com",
                        FirstName="First",
                        Id=2,
                        LastName="Last",
                        MiddleName="Middle",
                    }
                },
                new Task()
                {
                    CreatedDate = new DateTime(2010,1,1,1,1,1),
                    CreatorId = 1,
                    //Description ="Description 02 here",
                    Headline="Headline 02 here",
                    Id = 1,
                    LastEditedDate= new DateTime(2010,1,1,1,1,1),
                    LastEditOwnerId=1,
                    Name = "Name here",
                    NumViews = 3,
                    Script = @"<ScriptHere>script2</ScriptHere>",
                    // TODO: Tags
                    // TODO: TaskComments
                    User = new User()
                    {
                        Email ="one@hotmail.com",
                        FirstName="First",
                        Id=2,
                        LastName="Last",
                        MiddleName="Middle",
                    }
                }

            };
            return tasks;
        });

    return context;
}
protected internal User CreateDummyUser()
{
    User user = new User()
    {
        Email = "email",
        FirstName = "First",
        LastName = "Last",
        MiddleName = "Middle"
    };

    return user;
}

Since I’m just mocking methods all I’m really doing here is using the moq Setup method (formerly known as Expect), and the Returns method to implement the behavior that I needed. The key to note here is that if you need to access the parameter(s) passed into the method, then you will have to used Returns and pass in a lambda expression that contains the behavior. In that lamdba you can name the parameter anything you want, but I would suggest you name it the same name that the method you are mocking names it. This makes it much more understandable what you are actually doing.

Sayed Ibrahim Hashimi

mocks | moq | unit testing Friday, March 26, 2010 5:10:46 AM (GMT Standard Time, UTC+00:00)  #     | 
Wednesday, December 09, 2009

Unit testing and ASP.NET MVC Comments

Unit testing has been around for quite a while and a lot of developers have created their own unit tests. Even being a simple concept and having been around for some time I still think that unit tests are misunderstood, poorly written and don't follow best practices by a majority of developers. I'm not a unit testing expert but there are some guidelines that I try to follow when I create unit tests some of those are listed below (in no particular order).

If you make your unit tests too long and to test more than one item then they will become a pain to maintain, which leads to them being ignored and eventually to a complete breakdown to your testing effort in general.

I have recently been thinking more about how unit tests are being used throughout software development. Also I've been reading ASP.NET MVC in Action which coincidently puts a lot of emphasis on unit testing. I found a few quotes in Chapter 3 of that book, which covers Controllers, and thought that I would share them here because I agree with statements whole heartedly. Text in italics are my own comments.

Some of the key take aways: use mocks, and testing should be helpful to your development process not a hindrance.

Don't write unit tests just to write them or just to get the necessary coverage. Make sure that you write the test cases with a specific intent and that they are written well so that you will continue to maintain and execute them as a part of your CI build process.

Here are some unit tests that I've recently written that I'd like to use to show some concrete cases.

[Test]

public void TestGetConfigValue_ValueExistsRequiredTrue()

{

    string key = "83F216CD-1B3E-4fc1-9DA5-4A7D506AF7E8";

    string expectedValue = "C2D4E7CA-BE6B-4976-BFA9-50F5223C603A";

    ConfigurationManager.AppSettings[key] = expectedValue;

 

    string actualValue = ServiceConfigHelper.Instance.GetConfigValue(key, true);

    Assert.AreEqual(expectedValue, actualValue);

}

 

[Test]

public void TestGetConfigValue_ValueExistsRequiredFalse()

{

    string key = "0601AB1F-C78C-4c72-8648-B140D50BDDEC";

    string expectedValue = "92DCADBC-9618-49f3-A412-5C90A945903D";

    ConfigurationManager.AppSettings[key] = expectedValue;

 

    string actualValue = ServiceConfigHelper.Instance.GetConfigValue(key, false);

    Assert.AreEqual(expectedValue, actualValue);

}

 

[Test]

public void TestGetConfigValue_ValueDoesntExistRequiredFalse()

{

    string key = "2D4B5C66-BA37-4ab8-9B51-3F17CF348E6D";

 

    string actualValue = ServiceConfigHelper.Instance.GetConfigValue(key, false);

    Assert.IsNull(actualValue);

}

 

[ExpectedException(typeof(ConfigurationErrorsException))]

[Test]

public void TestGetConfigValue_ValueDoesntExistRequiredTrue()

{

    string key = "0511216E-3AA6-4b80-B215-E980A459466D";

 

    // ConfigurationErrorsException here

    string actualValue = ServiceConfigHelper.Instance.GetConfigValue(key, true);

}

 

[ExpectedException(typeof(ArgumentNullException))]

[Test]

public void TestGetConfigValue_KeyIsNull_RequiredFalse()

{

    string actualValue = ServiceConfigHelper.Instance.GetConfigValue(null, false);

}

 

[ExpectedException(typeof(ArgumentNullException))]

[Test]

public void TestGetConfigValue_KeyIsNull_RequiredTrue()

{

    string actualValue = ServiceConfigHelper.Instance.GetConfigValue(null, true);

}

 

// Setup method here

[SetUp]

public void Reset()

{

    ServiceConfigHelper.Instance.Reset();

    if (ConfigurationManager.AppSettings.HasKeys())

    {

        List<string> allKeys = new List<string>();

        foreach (string key in ConfigurationManager.AppSettings.Keys)

        {

            allKeys.Add(key);

        }

 

        foreach (string key in allKeys)

        {

            ConfigurationManager.AppSettings[key] = string.Empty;

        }

    }

}

Notice that each of these has a very descriptive name, if you saw the name of the test in the list of failed test cases you immediately know what to work on; you don't even have to look at the test. Notice that all of the tests are pretty small, and that each test case tests a very specific piece of functionality. Also you might have noticed that I use Guids in test cases I like to do this just to make sure that I have random data and that test cases don't conflict with each other.

Also I'd like to note that in the book the guys recommend reading Working Effectively with Legacy Code for good coverage of unit testing, I haven't read it but I might grab a copy.

 

Sayed Ibrahim Hashimi

ASP.NET MVC | unit testing Wednesday, December 09, 2009 6:37:17 AM (GMT Standard Time, UTC+00:00)  #     |