mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-14 17:54:42 +00:00
Add ValidationBehavior test coverage (#311)
This commit is contained in:
@@ -0,0 +1,189 @@
|
|||||||
|
using FluentAssertions;
|
||||||
|
using FluentValidation;
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using MediatR;
|
||||||
|
using Moq;
|
||||||
|
using Shared.Application.Behaviors;
|
||||||
|
|
||||||
|
namespace Shared.Application.Tests.Behaviors;
|
||||||
|
|
||||||
|
public class ValidationBehaviorTests
|
||||||
|
{
|
||||||
|
private static ValidationBehavior<ValidationTestRequest, ValidationTestResponse> CreateBehavior(
|
||||||
|
params IValidator<ValidationTestRequest>[] validators
|
||||||
|
) => new(validators);
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Handle_WhenRequestPassesAllValidators_InvokesNextUnchanged()
|
||||||
|
{
|
||||||
|
Mock<IValidator<ValidationTestRequest>> validatorMock = new();
|
||||||
|
validatorMock
|
||||||
|
.Setup(v =>
|
||||||
|
v.ValidateAsync(
|
||||||
|
It.IsAny<ValidationContext<ValidationTestRequest>>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(new ValidationResult());
|
||||||
|
ValidationBehavior<ValidationTestRequest, ValidationTestResponse> behavior = CreateBehavior(
|
||||||
|
validatorMock.Object
|
||||||
|
);
|
||||||
|
ValidationTestRequest request = new("aaron");
|
||||||
|
ValidationTestResponse response = new("ok");
|
||||||
|
bool nextInvoked = false;
|
||||||
|
RequestHandlerDelegate<ValidationTestResponse> next = () =>
|
||||||
|
{
|
||||||
|
nextInvoked = true;
|
||||||
|
return Task.FromResult(response);
|
||||||
|
};
|
||||||
|
|
||||||
|
ValidationTestResponse result = await behavior.Handle(
|
||||||
|
request,
|
||||||
|
next,
|
||||||
|
CancellationToken.None
|
||||||
|
);
|
||||||
|
|
||||||
|
nextInvoked.Should().BeTrue();
|
||||||
|
result.Should().BeSameAs(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Handle_WhenValidatorReportsFailures_ThrowsValidationExceptionAndNeverInvokesNext()
|
||||||
|
{
|
||||||
|
ValidationFailure failure = new("Name", "is required");
|
||||||
|
Mock<IValidator<ValidationTestRequest>> validatorMock = new();
|
||||||
|
validatorMock
|
||||||
|
.Setup(v =>
|
||||||
|
v.ValidateAsync(
|
||||||
|
It.IsAny<ValidationContext<ValidationTestRequest>>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(new ValidationResult([failure]));
|
||||||
|
ValidationBehavior<ValidationTestRequest, ValidationTestResponse> behavior = CreateBehavior(
|
||||||
|
validatorMock.Object
|
||||||
|
);
|
||||||
|
ValidationTestRequest request = new("");
|
||||||
|
bool nextInvoked = false;
|
||||||
|
RequestHandlerDelegate<ValidationTestResponse> next = () =>
|
||||||
|
{
|
||||||
|
nextInvoked = true;
|
||||||
|
return Task.FromResult(new ValidationTestResponse("ok"));
|
||||||
|
};
|
||||||
|
|
||||||
|
Func<Task> act = async () => await behavior.Handle(request, next, CancellationToken.None);
|
||||||
|
|
||||||
|
(await act.Should().ThrowAsync<ValidationException>()).Which.Errors.Should()
|
||||||
|
.ContainSingle()
|
||||||
|
.Which.Should()
|
||||||
|
.BeSameAs(failure);
|
||||||
|
nextInvoked.Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Handle_WhenMultipleValidatorsAreRegistered_RunsAllOfThem()
|
||||||
|
{
|
||||||
|
Mock<IValidator<ValidationTestRequest>> firstValidatorMock = new();
|
||||||
|
firstValidatorMock
|
||||||
|
.Setup(v =>
|
||||||
|
v.ValidateAsync(
|
||||||
|
It.IsAny<ValidationContext<ValidationTestRequest>>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(new ValidationResult());
|
||||||
|
Mock<IValidator<ValidationTestRequest>> secondValidatorMock = new();
|
||||||
|
secondValidatorMock
|
||||||
|
.Setup(v =>
|
||||||
|
v.ValidateAsync(
|
||||||
|
It.IsAny<ValidationContext<ValidationTestRequest>>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(new ValidationResult());
|
||||||
|
ValidationBehavior<ValidationTestRequest, ValidationTestResponse> behavior = CreateBehavior(
|
||||||
|
firstValidatorMock.Object,
|
||||||
|
secondValidatorMock.Object
|
||||||
|
);
|
||||||
|
ValidationTestRequest request = new("aaron");
|
||||||
|
RequestHandlerDelegate<ValidationTestResponse> next = () =>
|
||||||
|
Task.FromResult(new ValidationTestResponse("ok"));
|
||||||
|
|
||||||
|
await behavior.Handle(request, next, CancellationToken.None);
|
||||||
|
|
||||||
|
firstValidatorMock.Verify(
|
||||||
|
v =>
|
||||||
|
v.ValidateAsync(
|
||||||
|
It.IsAny<ValidationContext<ValidationTestRequest>>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
),
|
||||||
|
Times.Once()
|
||||||
|
);
|
||||||
|
secondValidatorMock.Verify(
|
||||||
|
v =>
|
||||||
|
v.ValidateAsync(
|
||||||
|
It.IsAny<ValidationContext<ValidationTestRequest>>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
),
|
||||||
|
Times.Once()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Handle_WhenMultipleValidatorsReportFailures_AggregatesErrorsFromAll()
|
||||||
|
{
|
||||||
|
ValidationFailure firstFailure = new("Name", "is required");
|
||||||
|
ValidationFailure secondFailure = new("Name", "must be at least 3 characters");
|
||||||
|
Mock<IValidator<ValidationTestRequest>> firstValidatorMock = new();
|
||||||
|
firstValidatorMock
|
||||||
|
.Setup(v =>
|
||||||
|
v.ValidateAsync(
|
||||||
|
It.IsAny<ValidationContext<ValidationTestRequest>>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(new ValidationResult([firstFailure]));
|
||||||
|
Mock<IValidator<ValidationTestRequest>> secondValidatorMock = new();
|
||||||
|
secondValidatorMock
|
||||||
|
.Setup(v =>
|
||||||
|
v.ValidateAsync(
|
||||||
|
It.IsAny<ValidationContext<ValidationTestRequest>>(),
|
||||||
|
It.IsAny<CancellationToken>()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(new ValidationResult([secondFailure]));
|
||||||
|
ValidationBehavior<ValidationTestRequest, ValidationTestResponse> behavior = CreateBehavior(
|
||||||
|
firstValidatorMock.Object,
|
||||||
|
secondValidatorMock.Object
|
||||||
|
);
|
||||||
|
ValidationTestRequest request = new("");
|
||||||
|
RequestHandlerDelegate<ValidationTestResponse> next = () =>
|
||||||
|
Task.FromResult(new ValidationTestResponse("ok"));
|
||||||
|
|
||||||
|
Func<Task> act = async () => await behavior.Handle(request, next, CancellationToken.None);
|
||||||
|
|
||||||
|
(await act.Should().ThrowAsync<ValidationException>()).Which.Errors.Should()
|
||||||
|
.BeEquivalentTo([firstFailure, secondFailure]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Handle_WhenNoValidatorsAreRegistered_InvokesNext()
|
||||||
|
{
|
||||||
|
ValidationBehavior<ValidationTestRequest, ValidationTestResponse> behavior = CreateBehavior();
|
||||||
|
ValidationTestRequest request = new("aaron");
|
||||||
|
ValidationTestResponse response = new("ok");
|
||||||
|
RequestHandlerDelegate<ValidationTestResponse> next = () => Task.FromResult(response);
|
||||||
|
|
||||||
|
ValidationTestResponse result = await behavior.Handle(
|
||||||
|
request,
|
||||||
|
next,
|
||||||
|
CancellationToken.None
|
||||||
|
);
|
||||||
|
|
||||||
|
result.Should().BeSameAs(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record ValidationTestRequest(string Name) : IRequest<ValidationTestResponse>;
|
||||||
|
|
||||||
|
public record ValidationTestResponse(string Value);
|
||||||
Reference in New Issue
Block a user