Search code examples
c#xunitnsubstitute

How to mock DbSet using xUnit and NSubstitute?


I started using xUnit and NSubstitute for my unit tests. I want to mock the following method.

public async Task<DecorationModel> GetDecorationWithId(string userId, string decorationId)
{
    var decoration = await _db.Decorations
        .Include(d => d.BgImage)
        .FirstOrDefaultAsync(d => d.Id == decorationId);

    if (decoration == null || decoration.OwnerId != userId)
        return null;

    return new DecorationModel
    {
        Id = decoration.Id,
        Name = decoration.Name,
        // Other stuff
    };
}

I attempted it but couldn't get it to work. My current test class is as follows;

public class DecorationServiceTests
{
    private readonly DecorationService _subject;
    private readonly IAppDbContext _db = Substitute.For<IAppDbContext>();
    private readonly DbSet<Decoration> _decorationDbSet = Substitute.For<DbSet<Decoration>, IQueryable<Decoration>>();

    public DecorationServiceTests()
    {
        _subject = new DecorationService(_db);
    }

    [Fact]
    public async Task GetDecorationWithId_ShouldReturnDecoration_WhenExists()
    {
        // Arrange
        var userId = new Guid().ToString();

        var decorationId = new Guid().ToString();
        var decorations = new List<Decoration>()
        {
            new Decoration()
            {
                Id = decorationId,
                Name = "",
                OwnerId = userId,
            }
        };
        
        _db.Decorations.Returns(_decorationDbSet);
        _decorationDbSet.FirstOrDefaultAsync(t => t.Id == decorationId).Returns(decorations.FirstOrDefault());

        // Act
        var result = await _subject.GetDecorationWithId(userId, decorationId);

        // Assert
        Assert.Equal(result.Id, decorations[0].Id);
    }
}

However, I get the following error:

"The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider'. Only providers that implement 'IAsyncQueryProvider' can be used for Entity Framework asynchronous operations."

I searched on the web but couldn't find a good reference. How can I solve this?


Solution

  • I think you'll be going through a whole lot of pain and suffering if you are trying to mock DbSet. That comes straight from the docs of EFCore: https://learn.microsoft.com/en-us/ef/core/testing/#unit-testing

    Instead, you should be trying to use a real db or in-memory one.
    See the testing sample here: https://learn.microsoft.com/en-us/ef/core/testing/testing-sample