Autofixture Xunit should throw but doesnt

45 Views Asked by At

I'm writing unit tests for my services using autofixture and autodata. This is the test.

[Theory]
[AutoMoqData]
public async Task GetByIdAsync_WhenInvalidId_ShouldThrowNotFoundException(
    Mock<IReviewRepository> repositoryMock,
    IReviewService service)
    {
    // Arrange
    repositoryMock.Setup(r => r.GetByIdAsync(It.IsAny<int>()))
        .ReturnsAsync(value: null);

    // Act
    var act = () => service.GetByIdAsync(It.IsAny<int>());

    await act.Should().ThrowAsync<NotFoundException>()
        .WithMessage(ErrorMessages.REVIEW_NOT_FOUND);
        }

And this the service method being tested

public async Task<Review> GetByIdAsync(int id)
{
    var foundReview = await _repository.GetByIdAsync(id);

    if (foundReview == null)
    {
        Log.Error("Review with id = {@id} not found", id);
        throw new NotFoundException(ErrorMessages.REVIEW_NOT_FOUND);
    }

    return _mapper.Map<Review>(foundReview);
}

As you can see, i setup repository used in service to return null if any int is passed so that i can check that the service properly throws my custom exception with my constant error message. Then i create a delegate of my method and test what i need. But the test fails with message:

Message: 
    Expected a <SecondMap.Services.StoreManagementService.BLL.Exceptions.NotFoundException> to be thrown, but no exception was thrown.

  Stack Trace: 
    XUnit2TestFramework.Throw(String message)
    TestFrameworkProvider.Throw(String message)
    DefaultAssertionStrategy.HandleFailure(String message)
    AssertionScope.FailWith(Func`1 failReasonFunc)
    AssertionScope.FailWith(Func`1 failReasonFunc)
    AssertionScope.FailWith(String message)
    DelegateAssertionsBase`2.ThrowInternal[TException](Exception exception, String because, Object[] becauseArgs)
    AsyncFunctionAssertions`2.ThrowAsync[TException](String because, Object[] becauseArgs)
    ExceptionAssertionsExtensions.WithMessage[TException](Task`1 task, String expectedWildcardPattern, String because, Object[] becauseArgs)
    ReviewServiceTests.GetByIdAsync_WhenInvalidId_ShouldThrowNotFoundException(Mock`1 repositoryMock, IReviewService service) line 59
    --- End of stack trace from previous location ---

I've tested manually and the method works as expected so I think the problem is with autofixture setups, but cant understand it exactly yet. Here is AutoMoqDataAttribute in case it helps

public class AutoMoqDataAttribute : AutoDataAttribute
{
    public AutoMoqDataAttribute() : base(CreateFixture)
    {
            
    }

    private static IFixture CreateFixture()
    {
        var fixture = new Fixture();
        fixture.Customize(new AutoMoqCustomization { ConfigureMembers = true });
        return fixture;
    }
}
1

There are 1 best solutions below

0
glebtyanov On

Update: solution for TR;DR: change IService to Service.

Explanation: debugging has shown that if you pass interface of the service instead of the actual class, then autofixture will create Mock<IService> which will not act the way it is supposed to, so either change IService to Service or in case you really want to use service you can create customization like that

public class AutoMoqDataAttribute : AutoDataAttribute
{
    public AutoMoqDataAttribute() : base(CreateFixture)
    {
    }

    private static IFixture CreateFixture()
    {
        var fixture = new Fixture();
        fixture.Customize(new CompositeCustomization(
            new AutoMoqCustomization { ConfigureMembers = true },
            new ServiceCustomization()));
            return fixture;
    }
}

public class ServiceCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Register<IReviewService>(CreateReviewService);
        fixture.Register<IScheduleService>(CreateScheduleService);
        fixture.Register<IStoreService>(CreateStoreService);
        fixture.Register<IUserService>(CreateUserService);
    }

    private static ReviewService CreateReviewService() => new ReviewService(new Mock<IReviewRepository>().Object, new Mock<IMapper>().Object);

    private static ScheduleService CreateScheduleService() => new ScheduleService(new Mock<IScheduleRepository>().Object, new Mock<IMapper>().Object);

    private static StoreService CreateStoreService() => new StoreService(new Mock<IStoreRepository>().Object, new Mock<IMapper>().Object);

    private static UserService CreateUserService() => new UserService(new Mock<IUserRepository>().Object, new Mock<IMapper>().Object);
} 

But it really isn't preferable since you want to test actual service