Josh
Josh

Reputation: 10604

Unit testing controller with dependency injected service and repository

I have a pretty standard .NET MVC 3 application that uses Ninject for dependency injection. What I'm trying to unit test is the view that gets returned from the controller. There is a controller with a group service on it and the service has a repository. The service first checks to see if that group exists and if it does, it returns false along with an error. The controller then checks the boolean value to determine whether to move the user on to the listing of groups or stay on the current page and display a value.

Controller

[HttpPost]
public ActionResult AddGroup(Group g)
{
   string error = string.Empty;
   if (groupService.Save(g, CurrentUser, out error))
   {
       return RedirectToAction("GetGroups");
   }
   else
   {
       ViewData["Error"] = error;
       return View("AddGroup");
   }
}

Service

[Inject]
public IGroupRepository CurrentGroupRepo { get; set; }

public bool Save(Group group, string username, out string error)
{
    error = string.Empty;
    var found = CurrentGroupRepo.GetGroup(group.Descr, false);
    if (found != null)
    {
        error = "That group alread exists";
        return false;
    }

    if (group.CreatedBy == null || group.CreatedBy == string.Empty)
    {
        group.CreatedBy = username;
        group.CreatedOn = DateTime.Now;
        group.IsDeleted =false;
    }

    group.ModifiedBy = username;
    group.ModifiedOn = DateTime.Now;

    try
    {
        if (group.GroupID == 0)
        {
            CurrentGroupRepo.AddGroup(group);
        }
        else
        {
            CurrentGroupRepo.UpdateGroup(group);
        }
        return true;
    }
    catch (Exception ex)
    {
        error = ex.Message + ex.StackTrace;
        return false;
    }
}

Unit Test

[TestMethod]
public void TestAddGroup()
{
    // Arrange
    Mock<IUserService> uService = new Mock<IUserService>();
    Mock<IGroupService> gService = new Mock<IGroupService>();
    Mock<IObservationService> oService = new Mock<IObservationService>();
    Mock<IGroupRepository> mockGroupRepo = new Mock<IGroupRepository>();

        string error = string.Empty;
        List<Group> groups = new List<Group>();
        Group newGroup = new Group() { Descr = "Test Group 1234566", IsDeleted = false };
        Mock<IGroupRepository> mockGroupRepo = new Mock<IGroupRepository>();

        mockGroupRepo.Setup(cr => cr.GetGroups(It.IsAny<bool>())).Returns(
            delegate {
                return groups;
            });
        mockGroupRepo.Setup(cr => cr.GetGroup(It.IsAny<string>(), It.IsAny<bool>())).Returns(
            delegate(Group _group) {
                return groups.Where(f => f.Descr == _group.Descr).FirstOrDefault();
            });
        mockGroupRepo.Setup(cr => cr.AddGroup(newGroup)).Returns(
            delegate {
                groups.Add(newGroup);
                return newGroup;
            });
        gService.SetupGet(d => d.CurrentGroupRepo).Returns(mockGroupRepo.Object);


    AdminController controller = new AdminController(gService.Object, uService.Object, oService.Object);
    Group newGroup = new Group() { Descr = "Test Group 1234566", IsDeleted = false };
    var success = (ViewResult)controller.AddGroup(newGroup);
    Assert.IsTrue("GetGroups" == success.ViewName);

    var failure = (ViewResult)controller.AddGroup(newGroup);
    Assert.IsTrue("AddGroup" == failure.ViewName);
}

What I'm trying to test is that when I add one group, it should work and then when I add the same group, it should go to a different view. But right now, the unit test is using the real repository instead of a mocked one. How do I get the repository on the mocked service to be controlled by the unit test instead of actually using a real repository?

Upvotes: 0

Views: 2003

Answers (2)

Josh
Josh

Reputation: 10604

I ended up simplifying it a bit given Bronumski's advice. Here is the code and all seems to be working well again.

        [TestMethod]
    public void TestAddGroup() {
        // Arrange
        Group newGroup = new Group() { Descr = "Test Group 1234566", IsDeleted = false };
        gService.Setup(cr => cr.Save(It.IsAny<Group>(), It.IsAny<string>())).Returns("");

        AdminController controller = new AdminController(gService.Object, uService.Object, oService.Object);

        // Act 
        var success = (RedirectToRouteResult)controller.AddGroup(newGroup);
        Assert.IsTrue("GetGroups" == success.RouteValues["action"].ToString());

        gService.Setup(cr => cr.Save(It.IsAny<Group>(), It.IsAny<string>())).Returns("Error");
        var failure = (ViewResult)controller.AddGroup(newGroup);
        Assert.IsTrue("AddGroup" == failure.ViewName);
    }

Upvotes: 0

Bronumski
Bronumski

Reputation: 14272

If you are unit testing the Controller then you don't need to worry about the Repository because your service is mocked. As far as the controller is concerned there is no repository, all it knows about is the IGroupService contract.

I cannot see how your testis using a real repository given the code you have supplied. If you want to change the out come of your test you will need to provide some mocked responses to your IGroupService mock.

You need a second unit test for the GroupService that takes in a Mocked Repository.

IMO a unit test should only test a single class, everything else should either be mocked or be very simple dto objects. If you are doing more then it is an integration test.

Upvotes: 2

Related Questions