Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock FluentValidation ValidationResults in test

This is the method that I want to test:

public async Task<object> CreateExpense(CreateExpenseCommand command)
{
    var validationResults = await _validator.ValidateAsync(command);
    if (!validationResults.IsValid)
    {
        return validationResults.Errors.First().ToString();
    }
    //more code that is irrelevant for this post
}

To test this, I need to mock _validatior, which is defined as private readonly IValidator<CreateExpenseCommand> _validator; and is used through constructor injection.

I am using AutoFixture with AutoMoqCustomizations and Moq for mocking. Maybe I should use Moq exclusively?

This is what I try to do in the test:

[Fact]
public async Task CreateExpense_Success()
{
    //Arrange
    var service = _fixture.Fixture.Freeze<Mock<IValidator<CreateExpenseCommand>>>();
    service.Setup(x => x.Validate((CreateExpenseCommand)It.IsAny<IValidator<CreateExpenseCommand>>())).Returns(It.IsAny<ValidationResult>);
    
    //more code that is irrelevant for this post
}

However, this results in error:

System.NullReferenceException: 'Object reference not set to instance of an object'.

The error is pretty self-explanatory, but I don't know how to mock correctly.

Image of error:enter image description here

like image 637
thesystem Avatar asked Sep 03 '25 16:09

thesystem


2 Answers

I am very late to answer what solved it for me, but here goes:

namespace ExpenseTests.Application
{
    public class CreateExpenseServiceTests
    {
        private TestFixture _fixture;
        public CreateExpenseServiceTests()
        {
            _fixture = new TestFixture();
        }
        [Fact]
        public async Task CreateExpense_Success()
        {
            //Arrange
            var expense = _fixture.Fixture.Create<Expense>();
            CreateExpenseCommand command = _fixture.Fixture.Build<CreateExpenseCommand>()
                .With(c => c.ExpenseCategory, "HOTEL").Create();
            var validationResults = _fixture.Fixture.Create<ValidationResult>();

            var addMock = new Mock<ICreateExpense>();
            addMock.Setup(x => x.CreateExpense(command)).ReturnsAsync(expense);
            ICreateExpense addService = addMock.Object;

            var validatorMock = new Mock<IValidator<CreateExpenseCommand>>();
            validatorMock.Setup(x => x.Validate(It.IsAny<CreateExpenseCommand>())).Returns(validationResults);

            //Act
            var response = await addService.CreateExpense(command);

            //Assert
            Assert.Equal(typeof(Expense), response.GetType());
        }
    }
}

A lot of it is domain code that you can ignore. Let me know if you have further questions - hope it helps a bit.

like image 145
thesystem Avatar answered Sep 05 '25 05:09

thesystem


You need to return an object when doing the setup:

service.Setup(x => x.Validate(It.IsAny<IValidator<CreateExpenseCommand>>()))
    .Returns(<*1>);

*1 - Here return the object that you wish to be returned when you call ValidateAsync function. Don't do It.IsAny as it returns null which causes the NullReferenceException.

Also, you need to add virtual to the ValidateAsync method in order to let it be overridable.

like image 23
Misha Zaslavsky Avatar answered Sep 05 '25 05:09

Misha Zaslavsky