Making ASP.NET MVC code more testable using a Service Layer.

Common wisdom says regarding the Repository and Unit of Work patterns in ASP.NET MVC to implement it on top of Entity Framework. This is supposed to make your code more testable and more decoupled, which is always desirable.

Doing this, however, can be complicated. The general architecture of an application implementing these patterns (as seen on MSDN) is shown below:

Windows-Live-Writer_8c4963ba1fa3_CE3B_Repository_pattern_diagram_1df790d3-bdf2-4c11-9098-946ddd9cd884

Implementing all of these classes can of course be daunting. Trying to write testable, modular code and following best practices might  lead to an explosion of classes, especially if you’re trying to follow the Repository pattern and what not.

There is a simpler way to do this. Implementing only a Service layer allows you to write more testable, modular code without having to write several different layers of classes.

Now, the standard architecture that ASP.NET MVC generates with scaffolding looks like this:

standard-m-v-c

The Model is used by the Controller to render into the View. Much of the code you’ll need is auto-generated but it’s very hard to test if you need to add your own business logic.

If you write Services however, classes that encapsulate Entity Framework stuff to use in your controller, you have a mockable, testable chunk of business logic which can decouple much of your application. It would look something like this:

service-example (1)

Each model would have a service. However, you can have a Service for a logical group of Models as well, that is up to you. Each Service will implement an interface, which can be mocked in unit tests and injected into the controllers.

For example, if you have a Student model and an Instructor model such that one Instructor has many Students and each model has it’s own controller. We can write a Service class for each model that uses the DbContext as it’s repository and unit of work. This way we will still be using the Repository and Unit of Work patterns, but not repeating stuff.

These services can then be injected into the Controller as you see fit and the reason we define interfaces for each service, is precisely so we can inject mock implementations of the services in the Controller.

Consider our Student-Instructor example. Say that you want a list of Students for that Instructor displayed when the user views details about that Instructor. You want this list to be always arranged by the Students’ names in alphabetical order.

We write a function in the InstructorService class to do this, let’s call it GetStudents(int instId) that simply returns a list of students with that instructor. It may seem simple but there are a lot of benefits to having such small functions. The first being that we don’t have to re-write the query each time we use it. For instance, we can use it in a normal Action method or we could use it in a Web API controller.

The normal Details action method, that renders a View that shows Instructor Details would be modified thus:

// GET: Instructors/Details/5
public ActionResult Details(int? id)
{
        if (id == null)
        {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }

        // I'm using id ?? 0 to convert int into a nullable int.
        Instructor instructor = instructorService.FindById(id ?? 0);
        ViewBag.students = instructorService.GetStudents(id ?? 0);

        if (instructor == null)
        {
                return HttpNotFound();
        }
        return View(instructor);
}

Where the instructorService object is injected into the Controller or initialized somewhere else. I’m using a ViewBag here but I should probably use ViewModels.

Regardless, we can easily test the behavior of our Details action method using the following test:

[TestMethod]
public void TestMethod1()
{
        // Arrange
        Mock mock = new Mock();
        Instructor testInstructor = new Instructor() { Id = 1, Name = "Test Instructor" };
        Student testStudentA = new Student()
        {
                Id = 100,
                Name = "AAA",
                Instructor = testInstructor
        };

        Student testStudentB = new Student()
        {
                Id = 100,
                Name = "BBB",
                Instructor = testInstructor
        };

        mock.Setup(m => m.GetStudents(It.IsAny()))
                .Returns(new List() { testStudentA, testStudentB });

        mock.Setup(m => m.FindById(It.IsAny())).Returns(testInstructor);

        // This List is already arranged alphabetically.
        List expectedStudents = new List()
        {
                testStudentA,
                testStudentB
        };

        // Act
        InstructorsController controller = new InstructorsController(mock.Object);
        var result = controller.Details(testInstructor.Id) as ViewResult;
        List returnedStudents = result.ViewBag.students;

        // Assert
        CollectionAssert.AreEqual(expectedStudents, returnedStudents);
}

The test above may see large but a lot of it is simply initialization code, that would go in a different method. This test simply makes sure that the ViewBag contains the proper students.

What’s important to note here is that we are testing the controller and only the controller. Therefore we create Mocks of all of its dependencies, which in this case is the IInstructorService interface.

This means that we can also test the Service classes if we mock the DbContext class, i.e. the Model which they depend on.

An example of such a test, that tests the GetStudents() method is given below. I’ve omitted a lot of the setup code this time, so it should be easier to follow.

[TestMethod]
public void TestGetStudents()
{
        //
        Debug.WriteLine(mockDbSetInstructors.Object);
        List students = new List() { testStudentB, testStudentA, };
        Mock mock = new Mock();
        mock.Setup(m => m.GetAll()).Returns(students);

        // This list is in alphabetical order
        List expectedStudents = new List() { testStudentA, testStudentB };

        // Act
        // Real implementation of Instructor Service
        InstructorService service = new InstructorService(mockCtx.Object, mock.Object);
        List returnedStudents = service.GetStudents(testInstructor.Id).ToList();

        // Assert
        CollectionAssert.AreEqual(expectedStudents, returnedStudents);
}

Thus, with the inclusion of the Service classes, we don’t have too much of an explosion of classes and our code is a lot more modular and testable than it previously was.

Of course eventually you might have to use ViewModels and AutoMapper and other stuff, but simply having Service objects such as these can make things a lot more testable.

Advertisements

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 )

Google+ photo

You are commenting using your Google+ 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 )

Connecting to %s