Интеграционное тестирование с AutoMapper не может инициализировать конфигурацию

Каркасы и пакеты

.NETCoreApp 1.1
Xunit 2.2.0
AutoMapper 6.0.2
Microsoft.AspNetCore.TestHost 1.1.1
Microsoft.NET.Test.Sdk 15.0.0

Интеграционный тест

public class ControllerRequestsShould
{
    private readonly TestServer _server;
    private readonly HttpClient _client;

    public ControllerRequestsShould()
    {
        _server = new TestServer(new WebHostBuilder()
            .UseContentRoot(Constants.apiProjectRoot)
            .UseStartup<Startup>()
            .UseEnvironment(Constants.testingEnvironment));
        _client = _server.CreateClient();
        _client.BaseAddress = new Uri(Constants.localHostUri);
    }

    [Fact]
    public async Task CreateAnEntity()
    {
        // Arrange
        var entityForCreationDto = new entityForCreationDto { Code = "00001", Name = "Entity One" };
        var jsonContent = JsonConvert.SerializeObject(entityForCreationDto);
        var stringContent = new StringContent(jsonContent);
        stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");

        // Act
        var response = await _client.PostAsync("/api/controller", stringContent);
        response.EnsureSuccessStatusCode();

        // Assert
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
    }
}

Startup.cs

public class Startup
{
    public IConfigurationRoot Configuration { get; }

    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();
        Configuration = builder.Build();
    }

    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services
        services.AddMvc(setupAction =>
        {
            setupAction.ReturnHttpNotAcceptable = true;
            setupAction.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
            setupAction.InputFormatters.Add(new XmlDataContractSerializerInputFormatter());
        });

        // Db context configuration
        var connectionString = Configuration["ConnectionStrings:DefaultConnection"];
        services.AddDbContext<YourContext>(options =>
        {
            options.UseSqlServer(connectionString);
        });

        // Register services for dependency injection
        services.AddScoped<IYourRepository, YourRepository>();

        services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();

        services.AddScoped<IUrlHelper, UrlHelper>(implementationFactory =>
        {
            var actionContext =
                implementationFactory.GetService<IActionContextAccessor>().ActionContext;
            return new UrlHelper(actionContext);
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole();
        loggerFactory.AddDebug(LogLevel.Information);
        loggerFactory.AddNLog();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler(appBuilder =>
            {
                appBuilder.Run(async context =>
                {
                    var exceptionHandlerFeature = context.Features.Get<IExceptionHandlerFeature>();
                    if (exceptionHandlerFeature != null)
                    {
                        var logger = loggerFactory.CreateLogger("Global exception logger");
                        logger.LogError(500,
                            exceptionHandlerFeature.Error,
                            exceptionHandlerFeature.Error.Message);
                    }

                    context.Response.StatusCode = 500;
                    await context.Response.WriteAsync("An unexpected fault happened.  Try again later");
                });
            });
        }

        Mapper.Initialize(cfg =>
        {
            cfg.CreateMap<DataStore.Entities.Entity, Models.EntityDto>();
            cfg.CreateMap<Models.EntityDto, DataStore.Entities.Entity>();
            cfg.CreateMap<Models.EntityForCreationDto, DataStore.Entities.Entity>();
            cfg.CreateMap<DataStore.Entities.Entity, Models.EntityForCreationDto>();
        });

        app.UseMvc();
    }

проблема
Интеграционный тест не пройден после вызова метода контроллера:

var response = await _client.PostAsync("/api/controller", stringContent);

Сбой, потому что AutoMapper не был инициализирован. То, как я это понял, было то, что с TestServer имеет UseStartup метод, он должен использовать все службы, настроенные в API Startup.cs класс (UseContentRoot указывает на мой корень проекта API)

Это явно не происходит. Может кто-нибудь показать мне, как мне нужно настроить TestServer чтобы правильно подобрать конфигурацию AutoMapper, пожалуйста?

3 ответа

Вы должны указать сборку в методе ConfigureServices: var assembly = typeof(Program).GetTypeInfo().Assembly; services.AddAutoMapper(assembly); Я использую Модули Automapper, поэтому конфигурация сопоставления автоматически выбирается AutoMapper, но даже в этом случае вам все еще нужен вышеуказанный конфиг.

Или просто используйте эту строку

 services.AddAutoMapper(typeof(Startup));

вместо

var assembly = typeof(Program).GetTypeInfo().Assembly;
services.AddAutoMapper(assembly);

что на мой взгляд более понятно и чисто

Спасибо, это работа для меня. Кроме того, вы можете добавить параметры конфигурации, как это.

var assembly = typeof(Program).GetTypeInfo().Assembly;

        services.AddAutoMapper(cfg =>
        {
            cfg.AllowNullDestinationValues = true;
            cfg.CreateMap<ApplicationUser, ApplicationUserView> ().IgnoreAllPropertiesWithAnInaccessibleSetter();}, assembly);
Другие вопросы по тегам