web-dev-qa-db-fra.com

Asp.net core 2.1 unit test automapper?

Message d'erreur

Message: System.InvalidOperationException: Le mappeur n'est pas initialisé. Appelez Initialize avec la configuration appropriée. Si vous essayez d'utiliser des instances de mappeur via un conteneur ou autrement, assurez-vous de ne pas avoir d'appels aux méthodes statiques Mapper.Map. Si vous utilisez des méthodes d'extension ProjectTo ou UseAsDataSource, assurez-vous de transmettre le fichier IConfigurationProvider approprié. exemple.

Projet d'application

Définir un profil de mappage ( ApplicationMappingProfile.cs )

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

Enregistrer les services de l'applicateur automatique ( 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;
    }
}

Projet de test unitaire

Créez un serveur de test pour exécuter 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();
    }
}

Et testez ( 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);
    }
}

Cette méthode de test appelle la méthode /api/test/GetUsers/ qui utilise un service de applications (PermissionAppService.cs). Ce service d'application renvoie un dto mappé depuis une entité à l'aide de automapper. Il y a donc une exception ici.

Quand j'ai enlevé la ligne suivante:

ServiceCollectionExtensions.UseStaticRegistration = false;

Alors j'obtiens l'erreur suivante:

Message: System.InvalidOperationException: Le mappeur est déjà initialisé. Vous devez appeler Initialize une fois par domaine d'application/processus.

Cette erreur se produit, car plusieurs classes sont héritées de la classe ApiTestBase. Si je n'exécute que la méthode TestAuthorizedAccessAsync, il s'exécute sans problème.

La question peut être un peu compliquée, désolée pour cela :) n'hésitez pas à demander où vous ne comprenez pas.

SOURCE DU PROJET

Modifier

Cela fonctionne lorsque je lance un projet Web api utilisant appServices

Edit2

si j'ai ajouté les lignes suivantes au constructeur TestBase, un seul test échouait et d'autres passaient.

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

....

 enter image description here

5
  • Désactiver l'enregistrement statique de l'applicateur automatique et l'initialiser pour le transmettre en tant que paramètre au service d'application

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();
    }
...
  • Passer le mappeur en tant que paramètre pour le service d'application

UserApplicationServiceTests.cs

public class UserApplicationServiceTests : ApplicationTestBase
{
    private readonly IUserAppService _userAppService;

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

Pour éviter cette erreur:

Message: System.InvalidOperationException: Le mappeur est déjà initialisé. Vous devez appeler Initialize une fois par domaine d'application/processus.

Vous devez appeler Mapper.Reset() avant d’exécuter chaque test: 

Le mappeur statique.Initialize est destiné à être appelé une seule fois. Pour Réinitialiser la configuration du mappage statique

La réinitialisation ne doit pas être utilisée dans le code de production. Il est destiné à prendre en charge uniquement les scénarios de test.

Vous pouvez l'appeler au début du test unitaire, mais je vous recommande de le faire dans votre constrcutor de classe de test unitaire, comme ci-dessous:

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