Reputation:
I'm using FluentValidation to validate request. If the validation fails UseCaseHandler
should not be invoked. Within the UseCaseHandler
I'm usign IRepository
, and this is checked to see if the UseCaseHandler
gets invoked.
The Request validator
public class MyValidator: AbstractValidator<Request>
{
public MyValidator()
{
RuleFor(rq=> rq)
.Cascade(CascadeMode.Continue);
RuleFor(rq=> rq)
.Must(property => property.id != default(Guid))
.WithMessage(message => $"invalid id.")
.WithName(member => nameof(member.Id));
}
}
This is the test
[Fact]
public async Task Test()
{
Mock<IUnitOfWork> uowMock = new Mock<IUnitOfWork>();
Mock<IRepository> repositoryMock = new Mock<IRepository>(MockBehavior.Strict);
Mock<IValidator<Request>> validatorMock = new Mock<IValidator<Request>>(MockBehavior.Strict);
var request = new Request
{
Id = Guid.NewGuid()
};
validatorMock
.Setup(validator => validator.Validate(request))
.Returns(new ValidationResult());
repositoryMock
.Setup(repo => repo.SaveAsync(It.IsAny<object>()))
.Returns(Task.CompletedTask);
var sut = new UseCase(uowMock.Object, repositoryMock.Object, validatorMock.Object);
Func<Task> act = () => sut.UseCaseHandler(request);
await act.Should().NotThrowAsync();
repositoryMock.Verify(repo => repo.SaveAsync(It.IsAny<object>()), Times.Once);
}
I'm looking to write a test that will check the flow.
If the validation fails the test should fail and SaveAsync should not be called.
If the validation succeeds that the test should succeed also and SaveAsync should be called one time.
What is the way to write a test ?
UPDATE
This is the use case class definition.
UseCaseHandlerProxy
is a base abstract class which acts as a proxy
public class UseCase : UseCaseHandlerProxy<Request, Response>
{
private readonly IRepository _repository;
public UseCase(IUnitOfWork unitOfWork, IRepository repository, IValidator<Request> validator)
: base(unitOfWork, validator)
{
_repository = repository
}
public override async Task<Response> UseCaseHandler(Request request)
{
Order order = new Order();
order.Create();
await _repository.SaveAsync(order);
return new Response(order.Id);
}
}
This is the Request class definition
class Request
{
public Guid Id { get; set; }
}
Te response only returns the same Id
Proxy class
public abstract class UseCaseHandlerProxy<TRequest, TResponse> : IUseCaseHandler<TRequest, TResponse>
where TRequest : IRequest
where TResponse : Response
{
private IValidator<TRequest> Validator { get; }
protected internal IUnitOfWork UnitOfWork { get; }
public UseCaseHandlerProxy(IUnitOfWork unitOfWork, IValidator<TRequest> validator)
{
Validator = validator;
UnitOfWork = unitOfWork;
}
async Task<TResponse> IUseCaseHandler<TRequest, TResponse>.HandleAsync(TRequest request)
{
ValidationResult validationResult = await Validator.ValidateAsync(request);
TResponse response;
if (!validationResult.IsValid)
{
response = (TResponse)System.Activator.CreateInstance(typeof(TResponse));
validationResult.Errors.ToList().ForEach(error => response.AddError(error.PropertyName, error.ErrorMessage));
return response;
}
response = await UseCaseHandler(request);
return response;
}
public abstract Task<TResponse> UseCaseHandler(TRequest request);
}
Upvotes: 3
Views: 5516
Reputation: 247551
Given the flow you want to test I would say that you are invoking the wrong member.
Cast the sut
to IUseCaseHandler<TRequest, TResponse>
to get access to HandleAsync
which is what does the desired flow.
For example the following verifies that if no validation error that repository invokes save.
[Fact]
public async Task UseCase_Should_Save() {
//Arrange
Mock<IUnitOfWork> uowMock = new Mock<IUnitOfWork>();
Mock<IRepository> repositoryMock = new Mock<IRepository>(MockBehavior.Strict);
Mock<IValidator<Request>> validatorMock = new Mock<IValidator<Request>>(MockBehavior.Strict);
var request = new Request {
Id = Guid.NewGuid()
};
validatorMock
.Setup(validator => validator.ValidateAsync(request, It.IsAny<CancellationToken>()))
.ReturnsAsync(new ValidationResult());
repositoryMock
.Setup(repo => repo.SaveAsync(It.IsAny<object>()))
.Returns(Task.FromResult((object)null));
var sut = new UseCase(uowMock.Object, repositoryMock.Object, validatorMock.Object) as IUseCaseHandler<Request, Response>;
//Act
Func<Task> act = () => sut.HandleAsync(request);
//Assert
await act.Should().NotThrowAsync();
repositoryMock.Verify(repo => repo.SaveAsync(It.IsAny<object>()), Times.Once);
}
The following verifies that if there are errors, then the repository does not save
[Fact]
public async Task UseCase_Should_Not_Save() {
//Arrange
var uowMock = new Mock<IUnitOfWork>();
var repositoryMock = Mock.Of<IRepository>();
var validatorMock = new Mock<IValidator<Request>>(MockBehavior.Strict);
var request = new Request {
Id = Guid.NewGuid()
};
var result = new ValidationResult();
result.Errors.Add(new ValidationFailure("SomeProperty", "SomeError"));
validatorMock
.Setup(validator => validator.ValidateAsync(request, It.IsAny<CancellationToken>()))
.ReturnsAsync(result);
var sut = new UseCase(uowMock.Object, repositoryMock, validatorMock.Object) as IUseCaseHandler<Request, Response>;
//Act
Func<Task> act = () => sut.HandleAsync(request);
//Assert
await act.Should().NotThrowAsync();
Mock.Get(repositoryMock).Verify(repo => repo.SaveAsync(It.IsAny<object>()), Times.Never);
}
Upvotes: 4