UserManager не создает пользователей в модульных тестах

#unit-testing #asp.net-core #asp.net-identity #moq #xunit

#модульное тестирование #asp.net-core #asp.net-identity #moq #xunit

Вопрос:

Я использую xUnit amp; Moq, и то, чего я пытаюсь достичь, обычно заключается в создании идентификатора пользователя, чтобы он хранился в базе данных Entity Framework в памяти.

Я установил функциональность начальных данных — она запускается, когда мой ASP.NET Приложение Core host запускается и работает безупречно — так что никаких проблем.

Но проблема возникает, когда я использую издевательский UserManager. Исключения не генерируются, пользователи просто не сохраняются.

Проверено при отладке тестов, DbContext возвращает 0 пользователей, также UserManager.FindByNameAsync выдает null результат.

Интересно, в чем причина. Может ли это быть связано с тем, как я собираю UserManager в конструкторе SeedDataTest класса?

 public class SeedDataTest
{
    private AppDbContext dbContext;
    private UserManager<ApplicationUser> userManager;
    private ITenantService tenantService;

    public SeedDataTest()
    {
        var options = new DbContextOptionsBuilder<AppDbContext>()
            .UseInMemoryDatabase(databaseName: "in_memory_db")
            .Options;

        dbContext = new AppDbContext(options);

        var userStore = new UserStore<ApplicationUser>(dbContext);

        userManager = new Mock<UserManager<ApplicationUser>>(
            userStore,
            new Mock<IOptions<IdentityOptions>>().Object,
            new Mock<IPasswordHasher<ApplicationUser>>().Object,
            new IUserValidator<ApplicationUser>[0],
            new IPasswordValidator<ApplicationUser>[0],
            new Mock<ILookupNormalizer>().Object,
            new Mock<IdentityErrorDescriber>().Object,
            new Mock<IServiceProvider>().Object,
            new Mock<ILogger<UserManager<ApplicationUser>>>().Object)
            .Object;

        tenantService = new Mock<TenantService>(dbContext).Object;
    }

    [Fact]
    public void Test1()
    {
        new TenantsCreator(dbContext).Create();
        new UserCreator(dbContext, tenantService, userManager).Create(); // stuck here
        new MembershipCreator(dbContext, userManager).Create();

        // unfinished
    }
}
  

И вот код из UserCreator

 public class UserCreator
{
    private AppDbContext _context;
    private ITenantService _tenantService;
    private UserManager<ApplicationUser> _userManager;

    public UserCreator(
        AppDbContext context,
        ITenantService tenantService,
        UserManager<ApplicationUser> userManager
        )
    {
        _context = context;
        _tenantService = tenantService;
        _userManager = userManager;
    }

    public void Create()
    {
        Task.Run(async () => await CreateUsers()).ConfigureAwait(false).GetAwaiter().GetResult();
    }

    private async Task CreateUsers()
    {
        ApplicationUser hostAdminUser = _context.Users.FirstOrDefault(x => x.UserName.Equals(SetupConsts.Users.AdminJoe.UserName));

        if (hostAdminUser == null)
        {
            hostAdminUser = new ApplicationUser()
            {
                FirstName = SetupConsts.Users.AdminJoe.FirstName,
                LastName = SetupConsts.Users.AdminJoe.LastName,
                UserName = SetupConsts.Users.AdminJoe.UserName,
                Email = SetupConsts.Users.AdminJoe.Email,
                EmailConfirmed = true,
                PasswordHash = new PasswordHasher<ApplicationUser>().HashPassword(hostAdminUser, SetupConsts.Users.Passwords.Default)
            };

            await _userManager.CreateAsync(hostAdminUser);
        }

        ApplicationUser secondaryUser = _context.Users.FirstOrDefault(x => x.UserName.Equals(SetupConsts.Users.JohnRoe.UserName));

        if (secondaryUser == null)
        {
            secondaryUser = new ApplicationUser()
            {
                FirstName = SetupConsts.Users.JohnRoe.FirstName,
                LastName = SetupConsts.Users.JohnRoe.LastName,
                UserName = SetupConsts.Users.JohnRoe.UserName,
                Email = SetupConsts.Users.JohnRoe.Email,
                EmailConfirmed = true,
                PasswordHash = new PasswordHasher<ApplicationUser>().HashPassword(secondaryUser, SetupConsts.Users.Passwords.Default)
            };

            await _userManager.CreateAsync(secondaryUser);
        }
    }
}
  

Ответ №1:

мы используем макетные фреймворки, позволяющие нам создавать макетные (замещенные) зависимости, а также указывать, что возвращается из классов / интерфейсов, используемых в нашем SUT.

Хорошим вариантом использования для этого является сама база данных. Мы всегда хотим знать точное состояние объектов во время их тестирования, и единственный способ сделать это — выразить их содержимое самостоятельно.

Вот тут-то и появляется Moq. Одна из его особенностей заключается в том, что он позволяет нам указывать результат вызовов методов.

Я полагаю, что вы видите 0 возвращаемых результатов, потому что вы используете реализацию класса moq (которая фактически не вызывает реализованный класс). Чтобы получить результат, вам нужно будет выполнить некоторую настройку:

 mockedClass.Setup(x => x.GetUserDetails(It.IsAny<int>())).Returns(new UserDetails());
  

Либо сделайте это таким образом, либо убедитесь, что вы передаете конкретную реализацию класса UserManager, а не издевательскую версию:

  userManager = new UserManager<ApplicationUser>
  

Надеюсь, это поможет

Комментарии:

1. требуется конкретная реализация — это было абсолютно так, действительно, большое вам спасибо!