qinking126
qinking126

Reputation: 11875

how to unit test controller when automapper is used?

here's my controller

    [POST("signup")]
    public virtual ActionResult Signup(UserRegisterViewModel user)
    {
        if (ModelState.IsValid)
        {
            var newUser = Mapper.Map<UserRegisterViewModel, User>(user);
            var confirmation = _userService.AddUser(newUser);

            if (confirmation.WasSuccessful)
                return RedirectToAction(MVC.Home.Index());
            else
                ModelState.AddModelError("Email", confirmation.Message);

        }
        return View(user);
    }

here's my unit test:

    [Test]
    public void Signup_Action_When_The_User_Model_Is_Valid_Returns_RedirectToRouteResult()
    {
        // Arrange
        const string expectedRouteName = "~/Views/Home/Index.cshtml";

        var registeredUser = new UserRegisterViewModel { Email = "[email protected]", Password = "123456789".Hash()};
        var confirmation = new ActionConfirmation<User>
                               {
                                   WasSuccessful = true,
                                   Message = "",
                                   Value = new User()
                               };
        _userService.Setup(r => r.AddUser(new User())).Returns(confirmation);

        _accountController = new AccountController(_userService.Object);

        // Act
        var result = _accountController.Signup(registeredUser) as RedirectToRouteResult;


        // Assert

        Assert.IsNotNull(result, "Should have returned a RedirectToRouteResult");
        Assert.AreEqual(expectedRouteName, result.RouteName, "Route name should be {0}", expectedRouteName);
    }

Unit test failed right here.

        var result = _accountController.Signup(registeredUser) as RedirectToRouteResult;

when I debug my unit test, I got following error message: "Missing type map configuration or unsupported mapping."

I think its because configuration is in web project, not the unit test project. what should I do to fix it?

Upvotes: 0

Views: 2469

Answers (3)

Vitaliy Markitanov
Vitaliy Markitanov

Reputation: 2438

Probably it is cool to abstract mapping into MappingEngine.

Sometimes I use following approach to IOC Automapper

In IOC builder:

  builder.RegisterInstance(AutoMapperConfiguration.GetAutoMapper()).As<IMapper>();

where GetAutoMapper is:

 public class AutoMapperConfiguration
    {
        public static IMapper GetAutoMapper()
        {
            var config = new MapperConfiguration(cfg =>
            {
                cfg.AddProfile<OrderModelMapperProfile>();
                cfg.AddProfile<OtherModelMapperProfile>();
                //etc;
               });
            var mapper = config.CreateMapper();
            return mapper;
        }
    }

And finally in Controller ctor

 public MyController(IMapper mapper)
        {
            _mapper = mapper;
        }

Upvotes: 0

Rob West
Rob West

Reputation: 5241

You should really use an interface for the mapping engine so that you can mock it rather than using AutoMapper otherwise it is an integration test not a unit test.

AutoMapper has an interface called IMappingEngine that you can inject into your controller using your IoC container like below (this example is using StructureMap).

class MyRegistry : Registry
{
    public MyRegistry()
    {
        For<IMyRepository>().Use<MyRepository>();
        For<ILogger>().Use<Logger>();

        Mapper.AddProfile(new AutoMapperProfile());
        For<IMappingEngine>().Use(() => Mapper.Engine);
    }
}

You will then be able to use dependency injection to inject AutoMapper's mapping engine into your controller, allowing you to reference your mappings like below:

[POST("signup")]
public virtual ActionResult Signup(UserRegisterViewModel user)
{
    if (ModelState.IsValid)
    {
        var newUser = this.mappingEngine.Map<UserRegisterViewModel, User>(user);
        var confirmation = _userService.AddUser(newUser);

        if (confirmation.WasSuccessful)
            return RedirectToAction(MVC.Home.Index());
        else
            ModelState.AddModelError("Email", confirmation.Message);

    }
    return View(user);
}

You can read more about this here: How to inject AutoMapper IMappingEngine with StructureMap

Upvotes: 2

tvanfosson
tvanfosson

Reputation: 532435

You need to have the mapper configured, so in your test class set up, not the per-test setup, call the code to set up the mappings. Note, you'll also probably need to modify your expectation for the user service call as the arguments won't match, i.e, they are different objects. Probably you want a test that checks if the properties of the object match those of the model being passed to the method.

Upvotes: 3

Related Questions