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.
If you have to do complex mapping behavior, it might be better to avoid using AutoMapper for that scenario. Reverse mapping can get very complicated very quickly, and unless it's very simple, you can have business logic showing up in mapping configuration.
If you've writing custom filters, routes, etc, you should unit test them, but not as part of your tests on a particular controller action. They should be tested in isolation.
AutoMapper is used whenever there are many data properties for objects, and we need to map them between the object of source class to the object of destination class, Along with the knowledge of data structure and algorithms, a developer is required to have excellent development skills as well.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With