Я использую FluentValidation с WebAPI в ядре DotNet 2. Я успешно написал тесты для валидатора, но сейчас пытаюсь смоделировать валидатор для моего контроллера.Контроллер выглядит следующим образом:
[Route("[controller]")]
public class SecurityController : Controller {
private readonly IValidator<AuthenticateRequest> _authenticateRequestValidator;
public SecurityController(IValidator<AuthenticateRequest> authenticateRequestValidator) {
_authenticateRequestValidator = authenticateRequestValidator;
}
[HttpPost]
[AllowAnonymous]
[Route("auth")]
public async Task<IActionResult> AuthenticateAsync([FromBody] AuthenticateRequest req) {
// Validate
var validator = await _authenticateRequestValidator.ValidateAsync(req);
if(!validator.IsValid) {
return BadRequest();
}
// ...snip
}
}
AuthenticateRequest выглядит следующим образом:
public class AuthenticateRequest {
public string Username { get; set; }
public string Password { get; set; }
}
И валидатор выглядит следующим образом:
public class AuthenticateRequestValidator : AbstractValidator<AuthenticateRequest> {
/// <summary>
/// Provides a validator for <see cref="AuthenticateRequest" />
/// </summary>
public AuthenticateRequestValidator() {
RuleFor(x => x.Username)
.NotNull()
.NotEmpty()
.WithMessage("Username is required");
RuleFor(x => x.Password)
.NotNull()
.NotEmpty()
.WithMessage("Password is required");
}
}
Он вводится в контроллер сстандартное DI Dot Net Core.Не публиковать код, поскольку он не имеет отношения к этой проблеме, так как это проблема тестирования.
Я тестирую с xunit, Moq и AutoFixture.Вот два теста:
public class SecurityControllerTests {
private readonly IFixture Fixture = new Fixture().Customize(new AutoMoqCustomization {ConfigureMembers = true});
private readonly Mock<IValidator<AuthenticateRequest>> authenticateRequestValidatorMock;
public SecurityControllerTests() {
authenticateRequestValidatorMock = Mock.Get(Fixture.Create<IValidator<AuthenticateRequest>>());
}
[Fact]
public async Task Authenticate_ValidatesRequest() {
// Arrange
var request = Fixture.Create<AuthenticateRequest>();
authenticateRequestValidatorMock
.Setup(x => x.ValidateAsync(It.Is<AuthenticateRequest>(v => v == request), default(CancellationToken)))
.Returns(() => Fixture.Create<Task<ValidationResult>>())
.Verifiable();
var controller = new SecurityController(authenticationServiceMock.Object, tokenisationServiceMock.Object, authenticateRequestValidatorMock.Object);
// Act
await controller.AuthenticateAsync(request);
// Assert
authenticateRequestValidatorMock.Verify();
}
[Fact]
public async Task Authenticate_Returns400_WhenUsernameValidationFails() {
// Arrange
var request = Fixture.Create<AuthenticateRequest>();
var validationResultMock = new Mock<ValidationResult>();
validationResultMock
.SetupGet(x => x.IsValid)
.Returns(() => true);
authenticateRequestValidatorMock
.Setup(x => x.ValidateAsync(It.Is<AuthenticateRequest>(v => v == request), default(CancellationToken)))
.Returns(() => new Task<ValidationResult>(() => validationResultMock.Object));
var controller = new SecurityController(authenticationServiceMock.Object, tokenisationServiceMock.Object, authenticateRequestValidatorMock.Object);
// Act
var result = await controller.AuthenticateAsync(request);
// Assert
var badRequestResult = Assert.IsType<BadRequestObjectResult>(result);
Assert.IsType<SerializableError>(badRequestResult.Value);
}
}
Мне нужно смоделировать ValidationResult
, чтобы я мог проигнорировать фактическую логику валидатора (которая тестируется в другом месте) и проверить логику контроллера.Внедрено много других зависимостей и гораздо больше кода, но вставленный код является сутью проблемы и дает те же результаты, когда все остальное удаляется.
Первый тест проходит, второй выполняется навсегда, когда он попадаетстрока var validator = await _authenticateRequestValidator.ValidateAsync(req);
в контроллере.
Стоит отметить, что ValidationResult.IsValid является виртуальным свойством только для чтения.
Что не так со вторым тестом?