How to Work With Generic Repositories on ASP.NET MVC and Unit Testing Them By Mocking

In this blog post we will see how to work with generic repositories on ASP.NET MVC and unit testing them by mocking with moq
2011-12-22 19:05
Tugberk Ugurlu


image

I have blogged about Generic Repository Pattern - Entity Framework, ASP.NET MVC and Unit Testing Triangle but that blog post only contains how we can implement that pattern on our DAL (Data Access Layer) project. Now, let’s see how it fits into our ASP.NET MVC application.

I created a sample project which basically has CRUD operations for Foo class and I put the complete code of the project on GitHub. https://github.com/tugberkugurlu/GenericRepoWebApp

So, in the previous blog post we have created our DAL project and repository classes based on our generic repository. I have created an ASP.NET MVC 3 Web Application project and a Visual Studio Test Project besides the DAL project. On the left had side, you can see how the project structure looks like inside solution explorer.

When we think about the project, we have repository classes which implements GenericRepositroy<C, T> abstract class and individual repository interfaces. So, we need a way to inject our concrete repository classes into our ASP.NET MVC application so that we make our application loosely coupled which means unit testing friendly.

Fortunately, ASP.NET MVC has been built with unit testing in mind so it makes dependency injection very easy with DependencyResolver class. DependencyResolver class Provides a registration point for dependency resolvers that implement IDependencyResolver or the Common Service Locator IServiceLocator interface. But we won’t be dealing with that class at all. Instead, we will use a third party dependency injector called Ninject. Ninject also has a package which benefits from DependencyResolver of ASP.NET MVC. We will use Nuget to bring down Ninject.

image

After we install the package, we will see a folder named App_Start added to our project. Inside that folder, open up the NinjectMVC3.cs file and go to RegisterServices method. In our case, here what we do:

/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel) {

    kernel.Bind<IFooRepository>().To<FooRepository>();
    kernel.Bind<IBarRepository>().To<BarReposiltory>();
}

This does some stuff behind the scenes and I won’t go into details here but I really recommend you to go and take a look at series of blog posts which Brad Wilson has done on ASP.NET MVC 3 Service Location. However, if we try to explain it with simplest words, Ninject news up the controller classes with parameters which we specify. In this case, if a controller constructor accepts a parameter which is type of IFooRepository, Ninject will give it FooRepository class and news it up. We will see why this is useful on unit testing stage.

When we look inside the RegisterServices method, we don’t see neither GenericRepository nor IGenericRepository because the way we implement them enables them to work behind the scenes.

As for the implementation, here how the controller looks like:

public class FooController : Controller {

    private readonly IFooRepository _fooRepo;
    
    public FooController(IFooRepository fooRepo) {
        _fooRepo = fooRepo;
    }

    public ViewResult Index() {

        var model = _fooRepo.GetAll();
        return View(model);
    }

    public ActionResult Details(int id) {

        var model = _fooRepo.GetSingle(id);
        if (model == null)
            return HttpNotFound();

        return View(model);
    }

    public ActionResult Edit(int id) {

        var model = _fooRepo.GetSingle(id);
        if (model == null)
            return HttpNotFound();

        return View(model);
    }

    [ActionName("Edit"), HttpPost]
    public ActionResult Edit_post(Foo foo) {

        if (ModelState.IsValid) {

            try {
                _fooRepo.Edit(foo);
                _fooRepo.Save();

                return RedirectToAction("details", new { id = foo.FooId });

            } catch (Exception ex) {
                ModelState.AddModelError(string.Empty, "Something went wrong. Message: " + ex.Message);
            }
        }

        //If we come here, something went wrong. Return it back.
        return View(foo);
    }

    public ActionResult Create() {

        return View();
    }

    [ActionName("Create"), HttpPost]
    public ActionResult Create_post(Foo foo) {

        if (ModelState.IsValid) {

            try {
                _fooRepo.Add(foo);
                _fooRepo.Save();

                return RedirectToAction("details", new { id = foo.FooId });

            } catch (Exception ex) {
                ModelState.AddModelError(string.Empty, "Something went wrong. Message: " + ex.Message);
            }
        }

        //If we come here, something went wrong. Return it back.
        return View(foo);

    }

    public ActionResult Delete(int id) {

        var model = _fooRepo.GetSingle(id);
        if (model == null)
            return HttpNotFound();

        return View(model);
    }

    [ActionName("Delete"), HttpPost]
    public ActionResult Delete_post(int id) {

        var model = _fooRepo.GetSingle(id);
        if (model == null)
            return HttpNotFound();

        _fooRepo.Delete(model);
        _fooRepo.Save();

        return RedirectToAction("Index");
    }
}

All of the action methods do some very basic stuff but one thing to notice here is the below code:

    private readonly IFooRepository _fooRepo;
    
    public FooController(IFooRepository fooRepo) {
        _fooRepo = fooRepo;
    }

As I mentioned before, controller constructor accepts a parameter which is type of IFooRepository and inside the constructor method, we expose the parameter for internal use of that controller class.

I have some views which corresponds to each action method and they work as expected.

Unit Testing

So, how do we unit test that controller without connecting to our database? When we think theoretically, what we need is a fake repository which implements IFooRepository interface so that we can pass that fake repository into our controller as a constructor parameter. Pay attention here that we still has no need for neither generic repository interface nor generic repository abstract class. We just need to fake FooRepository interface with fake data.

We will do this by mocking and creating mock objects. In order to do that, we will benefit from an awesome library called Moq.

As you can see inside the project on GitHub, I didn’t use NuGet to bring down the Moq because I tried and it failed over and over again. So, I put that inside the lib folder under the root directory and reference it from there.

After you reference the Moq library inside the Test application, create a class named FooControllerTest.cs. Here how it should look like at first:

[TestClass]
public class FooControllerTest {

}

So empty. We will start to fill it in with a mock of IFooReporsitory. Below, you can see the complete code which enables that:

[TestClass]
public class FooControllerTest {

    private IFooRepository fooRepo;

    [TestInitialize]
    public void Initialize() {

        //Mock repository creation
        Mock<IFooRepository> mock = new Mock<IFooRepository>();
        mock.Setup(m => m.GetAll()).Returns(new[] { 
            new Foo { FooId = 1, FooName = "Fake Foo 1" },
            new Foo { FooId = 2, FooName = "Fake Foo 2" },
            new Foo { FooId = 3, FooName = "Fake Foo 3" },
            new Foo { FooId = 4, FooName = "Fake Foo 4" }
        }.AsQueryable());

        mock.Setup(m => 
            m.GetSingle(
                It.Is<int>(i => 
                    i == 1 || i == 2 || i == 3 || i == 4
                )
            )
        ).Returns<int>(r => new Foo { 
            FooId = r,
            FooName = string.Format("Fake Foo {0}", r)
        });

        fooRepo = mock.Object;
    }

}

This Initialize method will run before all of the test methods run so we can work inside that method in order to mock our object.

In here, first I have setup a mock for GetAll method result and it returns 4 instances or Foo class.

Second, I do the same thing for GetSingle method. It looks a little different because it accepts a parameter type of int. What I am telling there is that: there are 4 instances I have here and if the parameter matches one of those instances, it will returns a Foo class which is a type of GetSingle method returns.

Lastly, I expose the mock object for internal use for the test class.

Now we have an IFooRepository instance we can work with. We have completed the hard part and now we can start writing our unit tests. Here is some of possible unit tests that we can have:

[TestClass]
public class FooControllerTest {

    private IFooRepository fooRepo;

    [TestInitialize]
    public void Initialize() {

        //Mock repository creation
        Mock<IFooRepository> mock = new Mock<IFooRepository>();
        mock.Setup(m => m.GetAll()).Returns(new[] { 
            new Foo { FooId = 1, FooName = "Fake Foo 1" },
            new Foo { FooId = 2, FooName = "Fake Foo 2" },
            new Foo { FooId = 3, FooName = "Fake Foo 3" },
            new Foo { FooId = 4, FooName = "Fake Foo 4" }
        }.AsQueryable());

        mock.Setup(m => 
            m.GetSingle(
                It.Is<int>(i => 
                    i == 1 || i == 2 || i == 3 || i == 4
                )
            )
        ).Returns<int>(r => new Foo { 
            FooId = r,
            FooName = string.Format("Fake Foo {0}", r)
        });

        fooRepo = mock.Object;
    }

    [TestMethod]
    public void is_index_returns_model_type_of_iqueryable_foo() {
        
        //Arrange
        //Create the controller instance
        FooController fooController = new FooController(fooRepo);

        //Act
        var indexModel = fooController.Index().Model;

        //Assert
        Assert.IsInstanceOfType(indexModel, typeof(IQueryable<Foo>));
    }

    [TestMethod]
    public void is_index_returns_iqueryable_foo_count_of_4() {

        //Arrange
        //Create the controller instance
        FooController fooController = new FooController(fooRepo);

        //Act
        var indexModel = (IQueryable<object>)fooController.Index().Model;

        //Assert
        Assert.AreEqual<int>(4, indexModel.Count());
    }

    [TestMethod]
    public void is_details_returns_type_of_ViewResult() {

        //Arrange
        //Create the controller instance
        FooController fooController = new FooController(fooRepo);

        //Act
        var detailsResult = fooController.Details(1);

        //Assert
        Assert.IsInstanceOfType(detailsResult, typeof(ViewResult));
    }

    [TestMethod]
    public void is_details_returns_type_of_HttpNotFoundResult() { 

        //Arrange
        //Create the controller instance
        FooController fooController = new FooController(fooRepo);

        //Act
        var detailsResult = fooController.Details(5);

        //Assert
        Assert.IsInstanceOfType(detailsResult, typeof(HttpNotFoundResult));
    }
}

When I run the all the test, I should see all of them pass and I do:

image

I hope this blog post gives you an idea. Stay tuned for others Winking smile

NOTE:

Entity Framework DbContext Generic Repository Implementation Is Now On Nuget and GitHub: http://www.tugberkugurlu.com/archive/entity-framework-dbcontext-generic-repository-implementation-is-now-on-nuget-and-github



Comments

Chris Marisic
by Chris Marisic on Thursday, Dec 22 2011 21:50:22 +02:00

I find unit testing MVC controllers to be completely lame.

I follow the orchestrator pattern: http://www.simple-talk.com/content/article.aspx?article=1371

MVC Controllers are supposed to be route drivers and not contain logic as per the true MVC pattern.

I use Selenium + SpecFlow to do integration level xBehavior BDD testing against controllers using real web browsers, and use Machine.Specifications + Machine.Fakes.Moq to do unit level xSpecification testing against my orchestrators.

Tugberk
by Tugberk on Thursday, Dec 22 2011 22:01:38 +02:00

@Chris

that's an opinion but I am sure that is not lame.

Chris Marisic
by Chris Marisic on Thursday, Dec 22 2011 22:20:06 +02:00

I guess I should have better elaborated why I find unit testing controllers lame. I find testing the ASP.NET MVC & Http abstraction to be lame. Especially with the returns being action results.

Unit testing an action results doesn't provide that much value. You still have no idea if your view will work. You still have no idea if real http usage will work because of the model binding abstraction. You also don't know if your code will work in reality (assuming it uses dependency injection). These abstractions are much better tested using BDD tools that allow real browser execution.

Testing the logic that your controllers will execute is substantially easier following the orchestrator pattern. Using the orchestrator patten lets you test your logic, while ignoring the implications of model binding, dependency injection, and view resolution. Those concerns are all integration level testing concerns.

James McLachlan
by James McLachlan on Friday, Dec 23 2011 17:00:24 +02:00

Don't forget to override Controller.Dispose(bool disposing) to dispose the repository.

Rangoric
by Rangoric on Friday, Dec 23 2011 17:14:14 +02:00

From the unit tests he is only testing the logic of the controller. Not the Model Binding or View Resolution, or DI system he plans to use. He isn't testing the action result at all. However since the action result is the return value generated by the logic, he is making sure that action result has the information that should be on it based on the logic of the controller.

He is not testing to see if the view will be found, the model will be right for the view, or anything of that nature. I don't test any of that either at the unit test level. Partially because I don't want to test the framework, but also because you are right, those are integration concerns.

Rangoric
by Rangoric on Friday, Dec 23 2011 17:16:46 +02:00

@James Why does the repository need to be disposed? Its not disposable itself so it shouldn't need it.

Tugberk
by Tugberk on Friday, Dec 23 2011 17:17:06 +02:00

@James

Good point. I love comments. 

I should have implemented that as well but I will update the post. Thanks!

Rangoric
by Rangoric on Friday, Dec 23 2011 17:55:28 +02:00

I know DbContext is, however the Repository is not disposable, so I was thinking it would be disposed elsewhere. I have been injecting the context via the constructor, and missed that you are just new()ing one up.

Tugberk
by Tugberk on Friday, Dec 23 2011 18:12:52 +02:00

@Rangoric

yep, as you can see, you will have Context property, which will have the DbContext class that you are passing, when you implement GenericRepository<C, T> abstract class.

Repositories are not disposable with the current implementation. However, if you implement IDisposable interface, they will be one. Then you will just need to complete the code as follows:

private bool disposed = false;

protected virtual void Dispose(bool disposing) {

    if (!this.disposed) {

        if (disposing) {

            context.Dispose();

        }

    }

    this.disposed = true;

}

public void Dispose() {

    Dispose(true);

    GC.SuppressFinalize(this);

}

Chris Marisic
by Chris Marisic on Wednesday, Dec 28 2011 22:26:42 +02:00

Something else I noticed:

  mock.Setup(m => m.GetSingle(It.Is<int>(i => ....

This setup would be alot better if you stuck

new[] { new Foo { FooId = 1, FooName = "Fake Foo 1" }, new Foo { FooId = 2, FooName = "Fake Foo 2" }, new Foo { FooId = 3, FooName = "Fake Foo 3" }, new Foo { FooId = 4, FooName = "Fake Foo 4" }
}

Into a static variable, then use the GetSingle method to query upon this directly.

This would allow you to verify reference equality of objects.
Tugberk
by Tugberk on Wednesday, Dec 28 2011 23:00:49 +02:00

@Chris

Does that check if there is any model or not inside the collection according ot id? I am unable to imagine this in my head. Can you fork my repo? https://github.com/tugberkugurlu/GenericRepoWebApp

CD
by CD on Tuesday, Jan 08 2013 21:02:26 +02:00

And how would you mock and test the Create, Update and Delete actions of your Controller?

Thanks in advance.

Regards.

thegenius
by thegenius on Saturday, Apr 12 2014 07:07:40 +00:00
Not very good. Just step thru code in debug against a list of required test criteria to see where it fails....or is that too much work for todays developers ?

New Comment