Saturday, 30 June 2012

xUnit Examples of Testing Controller Actions

We will see some examples of unit tests using xUnit testing framework to test controller actions. Let say we have these controller actions:
public ViewResult Add()
{            
    return View("Add", new StuffViewModel());
}


[HttpPost]
public ActionResult Add(StuffViewModel stuffViewModel)
{
    if (ModelState.IsValid)
    {
        Stuff stuff = mappingService.Map<StuffViewModel, Stuff>(stuffViewModel);
        stuffRepository.InsertOrUpdate(stuff);
        return RedirectToAction("List");
    }
    return View("Add", stuffViewModel);
}
As you can see both actions use same view. The first action is for displaying the view (HTTP GET) and the other is for handling submission from the view (HTTP POST).

Then below are the unit tests to cover both actions functionalities:
public class Add
{
    private Mock<IStuffRepository> stuffRepository;
    private Mock<IMappingService> mappingService;
    private StuffsController controller;

    public Add()
    {
        stuffRepository = new Mock<IStuffRepository>();
        mappingService = new Mock<IMappingService>();
        controller = new StuffsController(stuffRepository.Object, mappingService.Object);
    }


    [Fact]
    public void GET_should_return_add_view()
    {
        // Arrange

        // Act
        var result = controller.Add();

        // Assert
        var viewResult = Assert.IsType<ViewResult>(result);
        Assert.Equal("Add", viewResult.ViewName);
    }

    [Fact]
    public void GET_should_have_StuffViewModels()
    {
        // Arrange

        // Act
        var result = controller.Add();

        // Assert
        //Assert.IsAssignableFrom<StuffViewModel>(result.ViewData.Model); 
        Assert.IsType<StuffViewModel>(result.ViewData.Model);
    }

    [Fact]
    public void POST_should_save_to_database_if_model_is_valid()
    {
        // Arrange
        StuffViewModel stuffViewModel = new StuffViewModel { StuffID = 1 };
        Stuff stuff = new Stuff { StuffID = 1};
        mappingService.Setup(m => m.Map<StuffViewModel, Stuff>(It.IsAny<StuffViewModel>()))
                        .Returns(stuff);
                
        // Act
        controller.Add(stuffViewModel);

        //Assert
        stuffRepository.Verify(o => o.InsertOrUpdate(stuff), Times.Once());
    }

    [Fact]
    public void POST_should_redirect_to_list_view_after_saving()
    {
        // Arrange
        StuffViewModel stuffViewModel = new StuffViewModel { StuffID = 1 };
        Stuff stuff = new Stuff { StuffID = 1 };
        mappingService.Setup(m => m.Map<StuffViewModel, Stuff>(It.IsAny<StuffViewModel>()))
                        .Returns(stuff);

        // Act
        var result = controller.Add(stuffViewModel);

        // Assert
        var redirectToRouteResult = Assert.IsAssignableFrom<RedirectToRouteResult>(result);
        Assert.Equal("List", redirectToRouteResult.RouteValues["action"]);
    }

    [Fact]
    public void POST_if_not_valid_should_not_save_into_database()
    {
        // Arrange
        StuffViewModel stuffViewModel = new StuffViewModel { StuffID = 1 };
        Stuff stuff = new Stuff { StuffID = 1 };
        mappingService.Setup(m => m.Map<StuffViewModel, Stuff>(It.IsAny<StuffViewModel>()))
                        .Returns(stuff);
        controller.ModelState.AddModelError("key", "error");

        // Act
        var result = controller.Add(stuffViewModel);

        // Assert
        stuffRepository.Verify(o => o.InsertOrUpdate(stuff), Times.Never());
    }

    [Fact]
    public void POST_if_not_valid_should_return_to_add_view()
    {
        // Arrange
        StuffViewModel stuffViewModel = new StuffViewModel { StuffID = 1 };
        Stuff stuff = new Stuff { StuffID = 1 };
        mappingService.Setup(m => m.Map<StuffViewModel, Stuff>(It.IsAny<StuffViewModel>()))
                        .Returns(stuff);
        controller.ModelState.AddModelError("key", "error");

        // Act
        var result = controller.Add(stuffViewModel);

        // Assert
        var viewResult = Assert.IsType<ViewResult>(result);
        Assert.Equal("Add", viewResult.ViewName);
    }

    [Fact]
    public void POST_if_not_valid_should_return_view_with_StuffViewModel()
    {
        // Arrange
        StuffViewModel stuffViewModel = new StuffViewModel { StuffID = 1 };
        Stuff stuff = new Stuff { StuffID = 1 };
        mappingService.Setup(m => m.Map<StuffViewModel, Stuff>(It.IsAny<StuffViewModel>()))
                        .Returns(stuff);
        controller.ModelState.AddModelError("key", "error");

        // Act
        var result = controller.Add(stuffViewModel);

        // Assert
        var viewResult = Assert.IsType<ViewResult>(result);
        Assert.IsType<StuffViewModel>(viewResult.ViewData.Model);
    }
}

Thursday, 21 June 2012

Mocking AutoMapper in Unit Testing

This post will show how to mock AutoMapper with Moq in unit testing. Unit testing that has dependency to AutoMapper would require all of the mapping configurations be specified and run first before the actual mapping takes place (Mapper.Map(...) is called). These configurations would be burdensome and should not be included in unit testing. A unit test for a feature should only test that particular feature the developer has written. It should not test other services or functionalities. This is where the concept of mocking come up.

To be able to mock AutoMapper, we can use Dependency Injection to inject a mapper interface to the constructor of the calling code's class rather than using AutoMapper directly. Below is an example of such interface:
public interface IMappingService
{
    TDest Map<TSrc, TDest>(TSrc source) where TDest : class;
}

Then create an implementation class for the interface. Note on line 5 that for this class we specify AutoMapper directly.
public class MappingService : IMappingService
{
    public TDest Map<TSrc, TDest>(TSrc source) where TDest : class
    {
        return AutoMapper.Mapper.Map<TSrc, TDest>(source);
    }
}

Next, bind the interface with the concrete class. Below is an example of how to do it with Ninject:
kernel.Bind<IMappingService>().To<MappingService>();

Then whenever we want to do mapping, we call the interface's Map method instead of using the AutoMapper's Mapper.Map() method.
public ViewResult List()
{
    IEnumerable<Stuff> stuffs = stuffRepository.All;

    List<StuffViewModel> model = mappingService.Map<IEnumerable<Stuff>, List<StuffViewModel>>(stuffs);

    return View("List", model);
}
Please remember that mapping configurations need to be specified first before we could do any mapping. You can see this post to see how to set up the configurations.

Now we can mock the mapper in our unit test. In the example below I use xUnit testing framework. As you can see; first we create a mock instance from the interface, setup what the Map method will return and then pass the mock object to the class constructor of the feature to be tested (line 6, 25 and 28).
[Fact]
public void ListPageReturnsStuffViewModels()
{
    // Arrange
    Mock<IStuffRepository> stuffRepository = new Mock<IStuffRepository>();
    Mock<IMappingService> mappingService = new Mock<IMappingService>();

    List<Stuff> stuffs = new List<Stuff>();
    stuffRepository.Setup(r => r.All).Returns(stuffs.AsQueryable());

    var viewModelStuffs = new List<StuffViewModel> {
        new StuffViewModel { StuffID = 1/*,
                                Name= "Bip",
                                Description= "Colourful baby bip",
                                DateAdded = DateTime.Now,
                                UserID = 1 */
        },
        new StuffViewModel { StuffID = 2/*,
                                Name= "Socks",
                                Description= "Winter socks with animal figures",
                                DateAdded = DateTime.Now,
                                UserID = 1 */
        }
    };
    mappingService.Setup(m => m.Map<IEnumerable<Stuff>, List<StuffViewModel>>(It.IsAny<IEnumerable<Stuff>>()))
                    .Returns(viewModelStuffs);

    var controller = new StuffsController(stuffRepository.Object, mappingService.Object);


    // Act
    var result = controller.List() as ViewResult;
    //var model = result.ViewData.Model as List<StuffViewModel>;


    // Assert
    var model = Assert.IsType<List<StuffViewModel>>(result.ViewData.Model);
    Assert.Equal(2, model.Count);                
}