Why is IObjectMapper not initialised when unit testing a controller method?

2.5k Views Asked by At

I am using ASP.NET Boilerplate template for ASP.NET Core. I have some application services which I have successfully unit tested.

I now want to test a controller method which uses these application services. The controller method includes mapping operation like this:

var client = ObjectMapper.Map<ClientModel>(clientResponse.ClientSummary);

On executing this method, the test fails with an exception:

Message: Abp.AbpException : Abp.ObjectMapping.IObjectMapper should be implemented in order to map objects.

Interestingly, the stack trace begins with NullObjectMapper.Map.

I am using the same initialisation for AbpAutoMapper in the unit test module as is used in the Web.Mvc module:

Configuration.Modules.AbpAutoMapper().Configurators.Add(cfg =>
{
    cfg.AddProfiles(typeof(PortalTestModule).GetAssembly());
});

However, when executing in the context of the MVC application, the mapping operations don't cause the exception.

What is it that I am failing to initialise in the Test project in relation to AutoMapper?


I have created a repro project. See link. There is a test called GetFoos_Test that tests the controller method Index() on FoosController.

public async Task GetFoos_Test()
{
    var mockFooService = new Mock<IFooAppService>();
    // ...

    var fooController = new FooController(mockFooService.Object);

    var result = await fooController.Index();

    result.ShouldNotBeNull();
}

As per @aaron's answer, property injecting IObjectMapper on the controller does solve the original error. However, it doesn't use the mappings that are part of what I am trying to test. I have created mappings in the Initialize method of the MVC module like this:

public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(typeof(MyMvcModule).GetAssembly());

            Configuration.Modules.AbpAutoMapper().Configurators.Add(
               // Scan the assembly for classes which inherit from AutoMapper.Profile
               cfg =>
               {
                   cfg.AddProfiles(typeof(MyMvcModule).GetAssembly());

                   cfg.CreateMap<MyDto, MyModel>()
                       .ForMember(dest => dest.ReportTypeName, src => src.MapFrom(x => x.ReportType.Name));
... 

1

There are 1 best solutions below

5
aaron On

1. NullObjectMapper in new instances

IObjectMapper is property-injected into an instance (e.g. controller) when the instance is injected.

Since you cannot inject a controller in a unit test, you have to set ObjectMapper directly.

// using Abp.ObjectMapping;

var fooController = new FooController(mockFooService.Object);
fooController.ObjectMapper = LocalIocManager.Resolve<IObjectMapper>(); // Add this line

2. NullObjectMapper in injected instances

Add [DependsOn(typeof(AbpAutoMapperModule))] to your test module.

[DependsOn(typeof(AbpAutoMapperModule))]
public class MyTestModule : AbpModule
{
    // ...
}