Appendix C. Using a Mock Object Framework

The purpose of this appendix is to provide a brief overview of Mock Object Frameworks. Most classes that you want to test depend on multiple other classes. A Mock Object Framework makes it possible to isolate code so that it is testable.

You can use several popular Mock Object Frameworks with ASP.NET MVC including the following:

Moq—An open source Mock Object Framework that is relatively new but already popular. The ASP.NET MVC used Moq. You can download Moq from http://code.google.com/p/moq.

Rhino Mocks—The established open source Mock Object Framework. You can download Rhino Mocks from http://ayende.com/projects/rhinomocks/downloads.aspx.

Typemock Isolator—A commercial Mock Object Framework. Very powerful. You can learn more about Typemock Isolator from the company website at www.typemock.com.

In this appendix, I focus on describing Moq. The advantage of using Moq is that it is easy to understand. However, after you investigate Moq, I strongly encourage you to explore the other Mock Object Frameworks because each of the Mock Frameworks has its own strengths and weaknesses.

Understanding the Terminology

The terminology that surrounds Mock Object Frameworks can be confusing. Here is a brief glossary of terms:

Double—This is the generic term for any object that you use as a replacement for a production object.

Stub—A stub is an object that you use in place of a production object to make it easier to test your code. For example, you might create a stub for a repository class to make it easier to test a service class. You can generate stubs with a Mock Object Framework.

Mock—A mock is a class that you use to test the interaction between objects in an application. When you create a mock object, you specify expectations about how other objects interact with the mock object. If the other objects do not interact with the mock object in the way that you expect, the test fails.

Fake—A simplified version of a real production object. For example, a repository class that interacts with an in-memory database instead of an actual database. A fake is not created with a Mock Object Framework.

You can create two main types of objects with a Mock Object Framework: stubs and mocks. A stub is an object that you create to make it easier to perform a test. If one object depends on another object, you can stub the second object to make it possible to test the first object.

A mock object, in contrast, tests the interaction of the objects in your application. You create a mock of a production object to verify that the production object is called in the way that you expect. For example, you might want to verify the expectation that your service layer calls a particular method in your repository layer. In that case, you can mock the repository layer to verify this interaction.

In this appendix (and this book), I focus on using Mock Object Frameworks to create stubs and not mocks.

Note

This terminology is based on Gerard Meszaros’s terminology from his book xUnit Design Patterns: Refactoring Test Code. Also see the following two articles by Martin Fowler at http://martinfowler.com/bliki/TestDouble.html and http://martinfowler.com/articles/mocksArentStubs.html.

Installing Moq

You can download Moq from the following location: http://code.google.com/p/moq.

Note

The latest version of Moq, at the time that I write this, is version 3.1.

After you download Moq, make sure that you unblock the archive by right-clicking the file, selecting Properties, and clicking the Unblock button (see Figure C.1).

Figure C.1. Unblocking Moq

image

If you don’t unblock Moq, Visual Studio generates mysterious security errors when you try to use Moq in your test project.

Before you can use Moq in a test project, you need to add a reference to the Moq assembly. Follow these steps:

  1. After selecting your test project in the Solution Explorer window, select the menu option Project, Add Reference.
  2. In the Add Reference dialog, click the Browse tab.
  3. Browse to the folder where you download Moq and select the Moq.dll assembly (see Figure C.2).

    Figure C.2. Selecting the Moq assembly

    image
  4. Click the OK button.

After you complete these steps, use the Moq framework in your test project.

Using Moq to Create a Class from an Interface

Imagine that you want to create a Movie database application. Imagine, furthermore, that you have followed proper design patterns and divided your application logic into separate classes in which each class has a single responsibility.

You create a controller class, a service class, and a repository class. The controller class contains your controller logic, the service class contains your validation logic, and the repository class contains your data access logic.

The controller class is contained in Listing C.1.

Listing C.1. ControllersMovieController.cs (C#)

image

image


Listing C.1. ControllersMovieController.vb (VB)

image


And, the service class is contained in Listing C.2.

Listing C.2. ModelsMovieService.cs (C#)

image

image


Listing C.2. ModelsMovieService.vb (VB)

image

image


And, the repository class is contained in Listing C.3.

Listing C.3. ModelsMovieRepository.cs (C#)

image


Listing C.3. ModelsMovieRepository.vb (VB)

image


Now, imagine that you want to test the service class. You want to verify that if you pass a movie with a missing title to the service class Create() method, the method returns the value false.

Unfortunately, you can’t just create the service class in your test code because it depends on two other classes. The service class depends on an instance of the ModelStateDictionary class and an instance of the MovieRepository class. You can see these dependencies by looking at the constructor for the service class.

The ModelStateDictionary class does not present a problem. The ModelStateDictionary is just a specialized collection class. We can simply create a new instance of this class in our test code.

The MovieRepository class, on the other hand, does present a problem. We don’t want to create an instance of the actual MovieRepository class because the actual class interacts with a database. We need some way of creating a double for the actual MovieRepository class for the purposes of our test code.

Note

As an alternative to stubbing the MovieRepository class, we could fake the MovieRepository class. We could fake the MovieRepository class by implementing the IMovieRepository interface with a class that interacts with an in-memory database instead of the actual database. We explore this option in Chapter 5, “Understanding Models.”

We can use the Moq framework to quickly and easily create a stub for our MovieRepository class. The test in Listing C.4 illustrates how you can generate a stub from the IMovieRepository interface.

Listing C.4. ModelsMovieServiceTests.cs (C#)

image


Listing C.4. ModelsMovieServiceTests.vb (VB)

image


In Listing C.4, the stub for the MovieRepository class is created with this line of code:

(C#)

var repositoryStub = new Mock<IMovieRepository>();


(VB)

Dim repositoryStub As New Mock(Of IMovieRepository)()


Notice that the stub for the repository class is created from an interface: The stub is created from the IMovieRepository interface. (You also could create the stub from an abstract class.)

The repositoryStub variable does not represent the class that implements the IMovieRepository interface. You must use the expression repositoryStub.Object to get to the double for the MovieRepository. The responsitoryStub.Object class is the class that has the ListMovies() and CreateMovie() methods.

Returning Fake Values

In the previous section, you learned how you can test the Movie service class by creating a stub for the Movie repository class. By taking advantage of a Mock Object Framework, we isolated the Movie service class from the Movie repository class.

In this section, you learn how to return values from a stub object. In some situations, you need to make a stub class behave in a particular way to perform a test on the actual class being tested.

For example, imagine that you want to test the Movie controller class. This class is dependent on the Movie service class. To test the Movie class, we need to do more than simply stub the Movie service class. We need to fake values returned from calling methods on the Movie service class.

We want to test the controller logic in the Movie controller Create() action. The Create() action does one of two things depending on the value returned from calling the MovieService.CreateMovie() method. If CreateMovie() returns false—there is a validation error—then the Create() action should redisplay the form for creating a movie. On the other hand, if CreateMovie() returns true, then the Create() action should return a RedirectToAction result.

To test the Movie controller, we need to fake the value returned by the Movie service CreateMovie() method. We need to fake the value returned by the Movie service to fully test the Movie controller.

The two tests in Listing C.5 illustrate how you can fake the value returned by the CreateMovie() method.

Listing C.5. ControllersMovieControllerTests.cs (C#)

image

image


Listing C.5. ControllersMovieControllerTests.vb (VB)

image


The CreateWithBadMovieReturnsView() test verifies that when the Movie service CreateMovie() method returns false, the Movie controller returns a view result. The CreateWithGoodMovieReturnsRedirect() test verifies that when the Movie service CreateMovie() method returns true, the Movie controller returns a redirect to route result. (The RedirectToAction() method returns a RedirectToRouteResult.)

The following line of code causes the Movie service CreateMovie() method to return the value true:

(C#)

serviceStub.Setup(s => s.CreateMovie(It.IsAny<Movie>())).Returns(true);


(VB)

serviceStub.Setup(Function(s) s.CreateMovie(It.IsAny(Of Movie)())).Returns(True)


When any movie parameter is passed to the CreateMovie() method, the CreateMovie() method returns the value true.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.166.149