webentwicklung-frage-antwort-db.com.de

Asp.net Core 2.1 Unit Test-Automapper?

Fehlermeldung

Nachricht: System.InvalidOperationException: Mapper nicht initialisiert. Rufen Sie Initialize mit der entsprechenden Konfiguration auf. Wenn Sie versuchen, Mapper-Instanzen über einen Container oder auf andere Weise zu verwenden, stellen Sie sicher, dass die statischen Mapper.Map-Methoden nicht aufgerufen werden. Wenn Sie die Erweiterungsmethoden ProjectTo oder UseAsDataSource verwenden, müssen Sie den entsprechenden IConfigurationProvider übergeben Beispiel.

Anwendungsprojekt

Definieren Sie ein Zuordnungsprofil ( ApplicationMappingProfile.cs )

public class ApplicationMappingProfile : Profile
{
    public ApplicationMappingProfile()
    {
        CreateMap<User, UserListDto>();
        CreateMap<Permission, PermissionListDto>();
        // add auto mapper mapping configurations here
    }
}

Registrieren von Automapper-Diensten ( ApplicationServiceCollectionExtensions.cs )

public static class ApplicationServiceCollectionExtensions
{
    public static IServiceCollection AddKodkodApplication(this IServiceCollection services)
    {
        services.AddAutoMapper();

        //todo: add conventional registrar
        services.AddTransient<IUserAppService, UserAppService>();
        services.AddTransient<IPermissionAppService, PermissionAppService>();

        return services;
    }
}

Unit-Test-Projekt

Erstellen Sie einen Testserver zum Ausführen von Startup.cs ( ApiTestBase.cs )

public class ApiTestBase : TestBase
{
    protected static HttpClient Client;

    public ApiTestBase()
    {
        //if this is true, Automapper is throwing exception
        ServiceCollectionExtensions.UseStaticRegistration = false;

        Client = GetTestServer();
    }

    private static HttpClient GetTestServer()
    {
        if (Client != null)
        {
            return Client;
        }

        var server = new TestServer(
            new WebHostBuilder()
                .UseStartup<Startup>()
                .ConfigureAppConfiguration(config =>
                {
                    config.SetBasePath(Path.GetFullPath(@"../../.."));
                    config.AddJsonFile("appsettings.json", false);
                })
        );

        return server.CreateClient();
    }
}

Und teste ( AccountTests.cs ).

public class AccountTests : ApiTestBase
{
    [Fact]
    public async Task TestAuthorizedAccessAsync()
    {
        var responseLogin = await LoginAsTestUserAsync();
        var responseContent = await responseLogin.Content.ReadAsAsync<OkObjectResult>();
        var responseJson = JObject.Parse(responseContent.Value.ToString());
        var token = (string)responseJson["token"];

        var requestMessage = new HttpRequestMessage(HttpMethod.Get, "/api/test/GetUsers/");
        requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
        var responseGetUsers = await Client.SendAsync(requestMessage);
        Assert.Equal(HttpStatusCode.OK, responseGetUsers.StatusCode);

        var users = await responseGetUsers.Content.ReadAsAsync<PagedList<UserListDto>>();
        Assert.True(users.Items.Count > 0);
    }
}

Diese Testmethode ruft die /api/test/GetUsers/-Methode auf, die einen App-Service (PermissionAppService.cs) verwendet. Dieser App-Service gibt ein dto zurück, das mithilfe von automapper von der Entität zugeordnet wurde. Hier tritt also eine Ausnahme auf.

Als ich folgende Zeile entfernt habe:

ServiceCollectionExtensions.UseStaticRegistration = false;

Dann bekomme ich folgenden Fehler:

Nachricht: System.InvalidOperationException: Mapper ist bereits initialisiert. Sie müssen Initialize einmal pro Anwendungsdomäne/Prozess aufrufen.

Dieser Fehler tritt auf, weil mehrere Klassen von der ApiTestBase-Klasse geerbt werden. Wenn ich nur die TestAuthorizedAccessAsync-Methode ausführe, wird sie ohne Probleme ausgeführt.

Die Frage kann ein bisschen kompliziert sein, tut mir leid :) zögern Sie nicht zu fragen, wo Sie nicht verstehen.

PROJEKTQUELLE

Bearbeiten

Es funktioniert, wenn ich ein Web-API-Projekt starte, das appServices verwendet

Edit2

wenn ich dem TestBase-Konstruktor die folgenden Zeilen hinzufüge, schlägt nur ein Test fehl und andere sind bestanden.

public TestBase()
{
    lock (ThisLock)
    {
        Mapper.Reset();
        Client = GetTestServer();
    }

....

 enter image description here

5
  • Deaktivieren Sie die statische Registrierung des Automappers und initialisieren Sie sie, um sie als Parameter an den App-Service zu übergeben

TestBase.cs

public class TestBase
{
    private static Dictionary<string, string> _testUserFormData;

    protected readonly IMapper Mapper;
    protected readonly KodkodDbContext KodkodInMemoryContext;
    protected readonly ClaimsPrincipal ContextUser;
    protected readonly HttpClient Client;
    protected async Task<HttpResponseMessage> LoginAsTestUserAsync()
    {
        return await Client.PostAsync("/api/account/login",
            _testUserFormData.ToStringContent(Encoding.UTF8, "application/json"));
    }

    public TestBase()
    {
        // disable automapper static registration
        ServiceCollectionExtensions.UseStaticRegistration = false;

        // Initialize mapper
        Mapper = new Mapper(
            new MapperConfiguration(
                configure => { configure.AddProfile<ApplicationMappingProfile>(); }
            )
        );
        Client = GetTestServer();

        _testUserFormData = new Dictionary<string, string>
        {
            {"email", "[email protected]"},
            {"username", "testuser"},
            {"password", "123qwe"}
        };

        KodkodInMemoryContext = GetInitializedDbContext();
        ContextUser = GetContextUser();
    }
...
  • Übergeben Sie den Mapper als Parameter für den App-Service

UserApplicationServiceTests.cs

public class UserApplicationServiceTests : ApplicationTestBase
{
    private readonly IUserAppService _userAppService;

    public UserApplicationServiceTests()
    {
        var userRepository = new Repository<User>(KodkodInMemoryContext);
        _userAppService = new UserAppService(userRepository, Mapper);
    }
...
4

Um diesen Fehler zu vermeiden:

Nachricht: System.InvalidOperationException: Mapper wurde bereits initialisiert. Sie müssen Initialize einmal pro Anwendungsdomäne/prozess aufrufen.

Vor jedem Test müssen Sie Mapper.Reset() aufrufen: 

Die statische Mapper.Initialize soll nur einmal aufgerufen werden. Auf Die statische Mapping-Konfiguration zurücksetzen

Reset sollte nicht im Produktionscode verwendet werden. Es soll nur Testszenarien unterstützen.

Sie können es am Anfang des Komponententests aufrufen, aber ich empfehle, es in Ihrem Standardprüfprogramm für Komponententests wie folgt auszuführen:

public AccountTests()
{
    Mapper.Reset();
}
0
CodeNotFound