What are the possible problems with unit testing ASP.NET MVC code in the following way?

asp.net-mvc-3, unit-testing

Solution

Hmm. a few things here mate.

First, it looks like you're trying to test the a controller method. Great :)

So this means, that anything the controller needs, should be mocked. This is because

- You don't want to worry about what happens inside that dependency.

- You can verify that the dependency was called/executed.

Ok, so lets look at what you did and I'll see if i can refactor it to make it a bit more testable.

-REMEMBER- i'm testing the CONTROLLER METHOD, not the stuff the controller method calls/depends upon.

So this means I don't care about the service instance or the repository instance (which ever architectural way you decide to follow).

NOTE: I've kept things simple, so i've stripped lots of crap out, etc.

Interface

First, we need an interface for the repository. This can be implemented as a in-memory repo, an entity framework repo, etc.. You'll see why, soon.

public interface ILESRepository
{
    IQueryable<Person> GetAll();
}

Controller

Here, we use the interface. This means it's really easy and awesome to use a mock `IRepository` or a real instance.

public class PersonController : Controller
{
    private readonly ILESRepository _repository;

    public PersonController(ILESRepository repository)
    {
       if (repository == null)
       {
           throw new ArgumentNullException("repository");
       }
        _repository = repository;
    }

    public ActionResult Index(int id)
    {
        var model = _repository.GetAll<Person>()
            .FirstOrDefault(x => x.Id == id);

        var viewModel = new VMPerson(model);
        return View(viewModel);
    }
}

Unit Test

Ok - here's the magic money shot stuff. First, we create some Fake People. Just work with me here... I'll show you where we use this in a tick. It's just a boring, simple list of your `POCO`'s.

public static class FakePeople()
{
    public static IList<Person> GetSomeFakePeople()
    {
        return new List<Person>
        {
            new Person { Id = 1, Name = "John" },
            new Person { Id = 2, Name = "Fred" },
            new Person { Id = 3, Name = "Sally" },
        }
    }
}

Now we have the test itself. I'm using `xUnit` for my testing framework and `moq` for my mocking. Any framework is fine, here.

public class PersonControllerTests
{
    [Fact]
    public void GivenAListOfPeople_Index_Returns1Person()
    {
        // Arrange.
        var mockRepository = new Mock<ILESRepository>();
        mockRepository.Setup(x => x.GetAll<Person>())
                                   .Returns(
                                FakePeople.GetSomeFakePeople()
                                          .AsQueryable);
        var controller = new PersonController(mockRepository);
        controller.FakeOutContext();

        // Act.
        var result = controller.Index(person.Id) as ViewResult;

        // Assert.
        Assert.NotNull(result);
        var model = result.Model as VMPerson;
        Assert.NotNull(model);
        Assert.Equal(1, model.Person.Id);
        Assert.Equal("John", model.Person.Username);

        // Make sure we actually called the GetAll<Person>() method on our mock.
        mockRepository.Verify(x => x.GetAll<Person>(), Times.Once());
    }
}

Ok, lets look at what I did.

First, I arrange my crap. I first create a mock of the `ILESRepository`. Then i say: If anyone ever calls the `GetAll<Person>()` method, well .. don't -really- hit a database or a file or whatever .. just return a list of people, which created in `FakePeople.GetSomeFakePeople()`.

So this is what would happen in the controller ...

var model = _repository.GetAll<Person>()
                       .FirstOrDefault(x => x.Id == id);

First, we ask our mock to hit the `GetAll<Person>()` method. I just 'set it up' to return a list of people .. so then we have a list of 3 `Person` objects. Next, we then call a `FirstOrDefault(...)` on this list of 3 `Person` objects .. which returns the single object or null, depending on what the value of `id` is.

Tada! That's the money shot :)

Now back to the rest of the unit test.

We `Act` and then we `Assert`. Nothing hard there. For bonus points, I `verify` that we've actually called the `GetAll<Person>()` method, on the mock .. inside the Controller's `Index` method. This is a safety call to make sure our controller logic (we're testing for) was done right.

Sometimes, you might want to check for bad scenario's, like a person passed in bad data. This means you might never ever get to the mock methods (which is correct) so you `verify` that they were never called.

Ok - questions, class?

Problem

I've been looking at the way unit testing is done in the NuGetGallery. I observed that when controllers are tested, service classes are mocked. This makes sense to me because while testing the controller logic, I didn't want to be worried about the architectural layers below. After using this approach for a while, I noticed how often I was running around fixing my mocks all over my controller tests when my service classes changed. To solve this problem, without consulting people that are smarter than me, I started writing tests like this (don't worry, I haven't gotten that far): ``` public class PersonController : Controller { private readonly LESRepository _repository; public PersonController(LESRepository repository) { _repository = repository; } public ActionResult Index(int id) { var model = _repository.GetAll<Person>() .FirstOrDefault(x => x.Id == id); var viewModel = new VMPerson(model); return View(viewModel); } } public class PersonControllerTests { public void can_get_person() { var person = _helper.CreatePerson(username: "John"); var controller = new PersonController(_repository); controller.FakeOutContext(); var result = (ViewResult)controller.Index(person.Id); var model = (VMPerson)result.Model; Assert.IsTrue(model.Person.Username == "John"); } } ``` I guess this would be integration testing because I am using a real database (I'd prefer an inmemory one). I begin my test by putting data in my database (each test runs in a transaction and is rolled back when the test completes). Then I call my controller and I really don't care how it retrieves the data from the database (via a repository or service class) just that the Model to be sent to the view must have the record I put into the database aka my assertion. The cool thing about this approach is that a lot of times I can continue to add more layers of complexity without having to change my controller tests: ``` public class PersonController : Controller { private readonly LESRepository _repository; private readonly PersonService _personService; public PersonController(LESRepository repository) { _repository = repository; _personService = new PersonService(_repository); } public ActionResult Index(int id) { var model = _personService.GetActivePerson(id); if(model == null) return PersonNotFoundResult(); var viewModel = new VMPerson(model); return View(viewModel); } } ``` Now I realize I didn't create an interface for my PersonService and pass it into the constructor of my controller. The reason is 1) I don't plan to mock my PersonService and 2) I didn't feel I needed to inject my dependency since my PersonController for now only needs to depend on one type of PersonService. I'm new at unit testing and I'm always happy to be shown that I'm wrong. Please point out why the way I'm testng my controllers could be a really bad idea (besides the obvious increase in the time my tests will take to run).

Original source