Как макетировать весь слой приложения в тестовом методе? - PullRequest
0 голосов
/ 21 октября 2019

Я пытаюсь выполнить модульное тестирование моего ASP.NET Core WebAPI с проектом XUnit. Я использую 3-х уровневую архитектуру, где каждый слой находится в своем проекте библиотеки классов, а зависимости решаются с помощью внедрения зависимостей. Все работает нормально. Но когда я хочу выполнить модульное тестирование, я застрял с насмешками над уровнем логики и уровнем данных. Когда я запускаю тест, я получаю эту ошибку:

Сообщение: System.ArgumentException: аргументы конструктора не могут быть переданы для макетов интерфейса.

Мой Mock Object генерируется в этом классе:

public static class MockObjects
{
    public static  Mock<UserManager<TUser>> GetUserManagerMock<TUser>() where TUser : IdentityUser
    {
        return new Mock<UserManager<TUser>>(
            new Mock<IUserStore<TUser>>().Object,
            new Mock<IOptions<IdentityOptions>>().Object,
            new Mock<IPasswordHasher<TUser>>().Object,
            new IUserValidator<TUser>[0],
            new IPasswordValidator<TUser>[0],
            new Mock<ILookupNormalizer>().Object,
            new Mock<IdentityErrorDescriber>().Object,
            new Mock<IServiceProvider>().Object,
            new Mock<ILogger<UserManager<TUser>>>().Object);
    }

    public static Mock<SignInManager<TUser>> GetSignInManagerMock<TUser>() where TUser : IdentityUser
    {
        return new Mock<SignInManager<TUser>>(
            GetUserManagerMock<TUser>().Object,
            new Mock<IHttpContextAccessor>().Object,
            new Mock<IUserClaimsPrincipalFactory<TUser>>().Object,
            new Mock<IOptions<IdentityOptions>>().Object,
            new Mock<ILogger<SignInManager<TUser>>>().Object,
            new Mock<IAuthenticationSchemeProvider>().Object
            );
    }

    public static Mock<RoleManager<TIdentityRole>> GetRoleManagerMock<TIdentityRole>() where TIdentityRole : IdentityRole
    {
        return new Mock<RoleManager<TIdentityRole>>(
            new Mock<IRoleStore<TIdentityRole>>().Object,
            new IRoleValidator<TIdentityRole>[0],
            new Mock<ILookupNormalizer>().Object,
            new Mock<IdentityErrorDescriber>().Object,
            new Mock<ILogger<RoleManager<TIdentityRole>>>().Object);
    }

    public static Mock<ApplicationDbContext> GetDbContextMock()
    {
        return new Mock<ApplicationDbContext>(new DbContextOptions<ApplicationDbContext>());
    }

    public static Mock<TRepository> GetRepositoryMock<TRepository>() where TRepository : class
    {
        return new Mock<TRepository>(GetDbContextMock().Object);
    }

    public static Mock<ISmsService> GetSmsServiceMock()
    {
        return new Mock<ISmsService>(GetRepositoryMock<SmsRepository>().Object);
    }

    public static Mock<IEmailService> GetEmailServiceMock()
    {
        return new Mock<IEmailService>(GetRepositoryMock<EmailRepository>().Object);
    }

    public static Mock<IProfileService> GetProfileServiceMock()
    {
        return new Mock<IProfileService>(GetRepositoryMock<ProfileRepository>().Object);
    }

    public static Mock<IAccountService> GetAccountServiceMock()
    {
        return new Mock<IAccountService>(
            GetUserManagerMock<ApplicationUser>().Object,
            GetSignInManagerMock<ApplicationUser>().Object,
            GetSmsServiceMock().Object,
            GetEmailServiceMock().Object,
            new Mock<IConfiguration>().Object,
            GetProfileServiceMock().Object
            );
    }
}

Я реализовал этот класс после этой записи в блоге: https://dejanstojanovic.net/aspnet/2019/september/unit-testing-repositories-in-aspnet-core-with-xunit-and-moq/

Мой метод тестирования:

[Fact]
public async Task RegisterSimpleTest()
{
    // Arrange
    var mockService = MockObjects.GetAccountServiceMock().Object;
    var controller = new AccountController(mockService);
    var data = new EmailRegisterViewModel()
    {
        Email = "a@a.a",
        Password = "123456",
        ConfirmPassword = "123456"
    };
    // Act
    var result = await controller.Register(data) as ObjectResult;

    // Assert
    Assert.NotNull(result);
    Assert.Equal(200, result.StatusCode);
}

И действие контроллера, которое я хочу проверить:

[HttpPost]
public async Task<IActionResult> Register([FromBody]EmailRegisterViewModel model)
{
    if (!ModelState.IsValid)
    {
        var errors = ModelStateExtention.GetModelErrors(ModelState);
        return BadRequest(errors);
    }
    var user = new ApplicationUser()
    {
        UserName = model.Email,
        Email = model.Email
    };

    var result = await _accountService.EmailRegisterAsync(user, model.Password);
    return StatusResultExtension<ActionMessage>.GetStatusResult(result);
}

Как выполнить модульный тест? Что я не так делаю?

1 Ответ

0 голосов
/ 21 октября 2019

Вы пытаетесь передать аргументы mock, который не работает.

Если вы хотите передать mock из IAccountService вашему Controller в модульном тесте, который выможет сделать следующее:

[Fact]
    public async Task RegisterSimpleTest()
    {
        // Arrange
        var mockService = new Mock<IAccountService>();
        mockService.Setup(mock => mock.EmailRegisterAsync( // all you need to mock goes here)

        var controller = new AccountController(mockService.Object);
        var data = new EmailRegisterViewModel()
        {
            Email = "a@a.a",
            Password = "123456",
            ConfirmPassword = "123456"
        };
        // Act
        var result = await controller.Register(data) as ObjectResult;

        // Assert
        Assert.NotNull(result);
        Assert.Equal(200, result.StatusCode);
    }

Обратитесь к Быстрый запуск для получения информации о том, как mock поведение.

Кроме того, я бы посоветовал против настроить ваши mock в одном классе и использовать его везде. Вам нужна гибкость, чтобы изменить поведение вашего mock s за тест . Если вы находите установку громоздкой, вы можете заглянуть в AutoFixture.AutoMoq , который позволяет вам устанавливать только зависимости, для которых вам нужно указать поведение в конкретном тесте.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...