Unit Testing on Controller that uses AutoMapper

Steven picture Steven · May 8, 2013 · Viewed 11.4k times · Source

I am trying to unit test a UpdateUser Controller that uses AutoMapping. Here is the code for the controller

UpdateUserController

    private readonly IUnitOfWork _unitOfWork;
    private readonly IWebSecurity _webSecurity;
    private readonly IOAuthWebSecurity _oAuthWebSecurity;
    private readonly IMapper _mapper;

    public AccountController()
    {
        _unitOfWork = new UnitOfWork();
        _webSecurity = new WebSecurityWrapper();
        _oAuthWebSecurity = new OAuthWebSecurityWrapper();
        _mapper = new MapperWrapper();
    }

    public AccountController(IUnitOfWork unitOfWork, IWebSecurity webSecurity, IOAuthWebSecurity oAuthWebSecurity, IMapper mapper)
    {
        _unitOfWork = unitOfWork;
        _webSecurity = webSecurity;
        _oAuthWebSecurity = oAuthWebSecurity;
        _mapper = mapper;
    }

    //
    // Post: /Account/UpdateUser
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult UpdateUser(UpdateUserModel model)
    {
        if (ModelState.IsValid)
        {
            // Attempt to register the user
            try
            {
                var userToUpdate = _unitOfWork.UserRepository.GetByID(_webSecurity.CurrentUserId);
                var mappedModel = _mapper.Map(model, userToUpdate);

 **mappedModel will return null when run in test but fine otherwise (e.g. debug)**


                _unitOfWork.UserRepository.Update(mappedModel);
                _unitOfWork.Save();

                return RedirectToAction("Index", "Home");
            }
            catch (MembershipCreateUserException e)
            {
                ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
            }
        }
        return View(model);
    }

and this is my Unit Test UpdateUserControllerTest

[Fact]
    public void UserRepository_Update_User_Success()
    {
        Controller = new AccountController(UnitOfWork, WebSecurity.Object, OAuthWebSecurity.Object, Mapper);
        const string emailAsUserName = "[email protected]";
        const string password = "password";
        const string email = "[email protected]";
        const string emailNew = "[email protected]";
        const string firstName = "first name";
        const string firstNameNew = "new first name";
        const string lastName = "last name";
        const string lastNameNew = "new last name";

        var updatedUser = new User
            {
                Email = emailNew,
                FirstName = firstNameNew,
                LastName = lastNameNew,
                UserName = emailAsUserName
            };

        WebSecurity.Setup(
            s =>
            s.CreateUserAndAccount(emailAsUserName, password,
                                   new { FirstName = firstName, LastName = lastName, Email = email }, false))
                   .Returns(emailAsUserName);
        updatedUser.UserId = WebSecurity.Object.CurrentUserId;

        UnitOfWork.UserRepository.Update(updatedUser);
        UnitOfWork.Save();

        var actualUser = UnitOfWork.UserRepository.GetByID(updatedUser.UserId);
        Assert.Equal(updatedUser, actualUser);

        var model = new UpdateUserModel
            {
                Email = emailAsUserName,
                ConfirmEmail = emailAsUserName,
                FirstName = firstName,
                LastName = lastName
            };
        var result = Controller.UpdateUser(model) as RedirectToRouteResult;
        Assert.NotNull(result);
    }

I have a gut feel that when run in test mode, the mapper does not look at the mapper configuration that I have setup in Global.asax. Since the error only occur during the execution of unit test but not when running the website as is. I have created a IMappaer interface as a DI so I can mock it for testing purposes. I used Moq for Mocking and xUnit as a test framework, I have also installed AutoMoq which I have not used yet. Any idea? Thank you for looking at my lengthy post. Hope someone can help, been scratching my head for hours and reading up lots of posts.

Answer

Adam Rodger picture Adam Rodger · May 9, 2013

In your test you need to create a mocked version of your IMapper interface otherwise you aren't unit testing, you're integration testing. Then you just need to do a simple mockMapper.Setup(m => m.Map(something, somethingElse)).Returns(anotherThing).

If you want to use the real AutoMapper implementation in your tests then you need to set it up first. Your tests will not automatically pick up your Global.asax, you'll have to set up the mappings in the tests as well. When I'm integration testing like this I normally have a static AutoMapperConfiguration.Configure() method that I call in a test fixture setup. For NUnit this is the [TestFixtureSetUp] method, I think for xUnit you just put it in the constructor.