0

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 = "newuser@test.com", 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?

qinking126
  • 11,385
  • 25
  • 74
  • 124

3 Answers3

3

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.

tvanfosson
  • 524,688
  • 99
  • 697
  • 795
2

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

Community
  • 1
  • 1
Rob West
  • 5,189
  • 1
  • 27
  • 42
  • I've been using AutoMapper for a year, and I never realized that the IMappingEngine existed. I'd been getting by Mocking everything but AutoMapper, and always wondering why my test was failing, then go and look and find AutoMapper wasn't configured. Always annoying. Have the interface will be much better :) – GetFuzzy May 24 '14 at 21:13
0

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;
        }
Vitaliy Markitanov
  • 2,205
  • 1
  • 24
  • 23