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).
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
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.
Controller
Here, we use the interface. This means it’s really easy and awesome to use a mock
IRepositoryor a real instance.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.Now we have the test itself. I’m using
xUnitfor my testing framework andmoqfor my mocking. Any framework is fine, here.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 inFakePeople.GetSomeFakePeople().So this is what would happen in the controller …
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 3Personobjects. Next, we then call aFirstOrDefault(...)on this list of 3Personobjects .. which returns the single object or null, depending on what the value ofidis.Tada! That’s the money shot 🙂
Now back to the rest of the unit test.
We
Actand then weAssert. Nothing hard there.For bonus points, I
verifythat we’ve actually called theGetAll<Person>()method, on the mock .. inside the Controller’sIndexmethod. 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
verifythat they were never called.Ok – questions, class?