天天看點

Mocking .NET Objects with NUnit

NUnit is my Unit Testing tool of choice for .NET development. Microsoft provides a unit testing framework but it only works with some higher-end versions of Visual Studio. They’re so similar that it’s almost ridiculous that Microsoft created their own version.

(See one of my previous posts for more information on Automating NUnit with MSBuild .) In the Java world it’s fairly common to do Mocking to help make unit testing easier. I’ve written about using JMock for Unit Tesing in Java. In this post, I’d like to talk about a relatively new feature of NUnit which now supports Mocks out of the box.

What Are Mock Objects

Mock Objects are a technique that allow you to isolate classes from their dependencies for testing purposes. This isolation allows for fine-grained testing of single methods in a single class, possibly even before the dependent classes are fully implemented. This isolation allows your tests to run quickly and it makes testing small pieces of functionality much easier. When you’ve tested individual pieces of code in isolation you can have much higher confidence in larger-grained tests. This isolation becomes even more interesting when you are dealing with dependencies such as a data layer or a web service layer. External calls like that can be very time consuming or could fail if the remote system is down for maintenance.

One of the great things about using Mock Objects is that they force you to think about the dependencies that your classes and methods have. It forces you to think about the coupling between your classes. If you have high coupling then your code is often harder to test. If you have a loosely coupled design then testing and using Mock Objects is very much easier. Thinking about those design notions early can help you more easily manage change over time.

Maxims:

  • Good design is better than bad design
  • Loosely coupled objects are usually a better design than tightly coupled objects
  • Testing improves code quality and developer efficiency over time
  • Testing is easier with a loosely coupled designs

A Sample Project

We’re going to start with some simple code. We create a Domain object called Person and an interface for a Data Access object called IPersonRepository. Pretty simple at this point.

public class Person

{

public string Id;

public string FirstName;

public string LastName;

public Person(string newId, string fn, string ln)

{

Id = newId;

FirstName = fn;

LastName = ln;

}

}

public interface IPersonRepository

{

List<Person> GetPeople();

Person GetPersonById(string id);

}

Next we create a PersonService object. This would represent all of the business logic in our application. It would interact with the Data Access tier and return information to the UI layer for display.

We wire together our objects using Constructor based Dependency Injection . All of the dependent Objects are sent in through the constructor. This allows for the loose coupling since the PersonService doesn’t know about the Implementing class, but only the interface. Since it’s done in the constructor we can also never have an invalid PersonService as would be the case if there was a setter for the IPersonRepository implementation.

This is again a fairly straightforward implementation, but I hope enough to display the issue at hand.

public class PersonService

{

private IPersonRepository personRepos;

public PersonService(IPersonRepository repos)

{

personRepos = repos;

}

public List<Person> GetAllPeople()

{

return personRepos.GetPeople();

}

public List<Person> GetAllPeopleSorted()

{

List<Person> people = personRepos.GetPeople();

people.Sort(delegate(Person lhp, Person rhp) {

return lhp.LastName.CompareTo(rhp.LastName);

});

return people;

}

public Person GetPerson(string id)

{

try

{

return personRepos.GetPersonById(id);

}

catch (ArgumentException)

{

return null; // no person with that id was found

}

}

}  

Using Mocks with NUnit

Now we can start testing our PersonService. Notice that we haven’t even implemented the IPersonRepository yet. That way we can make sure that everything in our PersonService class works as expected without having to think about other layers of the application.

using System;

using System.Collections.Generic;

using NUnit.Framework;

using NUnit.Mocks;

[TestFixture]

public class PersonServiceTest

{

// The dynamic mock proxy that we will use to implement IPersonRepository

private DynamicMock personRepositoryMock;

// Set up some testing data

private Person onePerson = new Person("1", "Wendy", "Whiner");

private Person secondPerson = new Person("2", "Aaron", "Adams");

private List<Person> peopleList;

[SetUp]

public void TestInit()

{

peopleList = new List<Person>();

peopleList.Add(onePerson);

peopleList.Add(secondPerson);

// Construct a Mock Object of the IPersonRepository Interface

personRepositoryMock = new DynamicMock(typeof (IPersonRepository));

}

[Test]

public void TestGetAllPeople()

{

// Tell that mock object when the "GetPeople" method is

// called to return a predefined list of people

personRepositoryMock.ExpectAndReturn("GetPeople", peopleList);

// Construct a Person service with the Mock IPersonRepository

PersonService service = new PersonService(

(IPersonRepository) personRepositoryMock.MockInstance);

// Call methods and assert tests

Assert.AreEqual(2, service.GetAllPeople().Count);

}

[Test]

public void TestGetAllPeopleSorted()

{

// Tell that mock object when the "GetPeople" method is called to

// return a predefined list of people

personRepositoryMock.ExpectAndReturn("GetPeople", peopleList);

PersonService service = new PersonService(

(IPersonRepository) personRepositoryMock.MockInstance);

// This method really has "business logic" in it - the sorting of people

List<Person> people = service.GetAllPeopleSorted();

Assert.IsNotNull(people);

Assert.AreEqual(2, people.Count);

// Make sure the first person returned is the correct one

Person p = people[0];

Assert.AreEqual("Adams", p.LastName);

}

[Test]

public void TestGetSinglePersonWithValidId()

{

// Tell that mock object when the "GetPerson" method is called to

// return a predefined Person

personRepositoryMock.ExpectAndReturn("GetPersonById", onePerson, "1");

PersonService service = new PersonService(

(IPersonRepository) personRepositoryMock.MockInstance);

Person p = service.GetPerson("1");

Assert.IsNotNull(p);

Assert.AreEqual(p.Id, "1");

}

[Test]

public void TestGetSinglePersonWithInalidId()

{

// Tell that mock object when the "GetPersonById" is called with a null

// value to throw an ArgumentException

personRepositoryMock.ExpectAndThrow("GetPersonById",

new ArgumentException("Invalid person id."), null);

PersonService service = new PersonService(

(IPersonRepository) personRepositoryMock.MockInstance);

// The only way to get null is if the underlying IPersonRepository

// threw an ArgumentException

Assert.IsNull(service.GetPerson(null));

}

}

The PersonService doesn’t have a lot of logic in it, but I hope this illustrates how you easily can test various conditions using Mock objects. It also illustrates the idea of testing early by allowing you to test some code before all of the dependent objects are implemented.

While the Mocks built into NUnit might not be the most powerful or complete Mocking library out there, it should be sufficient for most uses. I’m sure they will continue to improve them over time as well, so I look forward to them becoming more powerful (and having better documentation) in the future.

Download Code Example:

NMock C# Example Project

For more on NUnit you might like to check out:

  • Pragmatic Unit Testing in C# with Nunit (Pragmatic Programmers)
    Mocking .NET Objects with NUnit
  • NUnit Pocket Reference (Pocket Reference (O’Reilly))
    Mocking .NET Objects with NUnit