Peter Szekeli
Peter Szekeli

Reputation: 2800

Mock EF dbContext async methods using NSubstitute 4.0

I got an error when trying to use NSubstitute on my DbContext. So far I used the EntityFramework.Testing package and the following code to set up my DbContext in the tests. It was quite simple, here's a sample from their site:

// Create some test data
var data = new List<Blog>
{
    new Blog{ Name = "BBB" },
    new Blog{ Name = "CCC" },
    new Blog{ Name = "AAA" }
};

// Create a DbSet substitute.
var set = Substitute.For<DbSet<Blog>, IQueryable<Blog>, IDbAsyncEnumerable<Blog>>()
                    .SetupData(data);

var context = Substitute.For<BloggingContext>();
context.Blogs.Returns(set);

This works nicely with version 3.1.0, but throws the following exception if I upgrade NSubstitute to 4.0.

System.MissingMethodException Method not found: 'System.__Canon NSubstitute.Arg.Any()'. NSubstitute.NSubstituteDbSetExtensions.SetupData[TEntity](DbSet'1 dbSet, ICollection'1 data, Func'2 find)

I wonder if there's a good solution to mock EF contexts with async support or should I just stick to 3.1.0.

Upvotes: 2

Views: 1134

Answers (1)

A.Ottavi
A.Ottavi

Reputation: 1

[TestFixture]
public class InvoicesControllerTests
{
    private InvoicesController _controller;
    private BdAgendaTChampionContext _context;

    [SetUp]
    public void SetupInvoice()
    {
        // Mock the context
        var options = new DbContextOptionsBuilder<BdAgendaTChampionContext>()
            .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
            .Options;

        _context = new BdAgendaTChampionContext(options);

        //// Seed the in-memory database with substitute data
        _context.Invoices.AddRange(Substitute.For<Invoice>());
        _context.InvoiceDetails.AddRange(Substitute.For<InvoiceDetail>());
        _context.Customers.AddRange(Substitute.For<Customer>());

        _context.SaveChanges();

        // Initialize the controller
        _controller = new InvoicesController(_context);
    }

    [TestFixture]
    public class Update : InvoicesControllerTests
    {
        [Test]
        public void InvoiceStatusID_ShouldReturnOK()
        {
            //Arrange
            int vKey = 1;
            var vUpdatedStatus = 30;
            var vValue = new { InvoiceStatusId = vUpdatedStatus };
            var vData = JsonConvert.SerializeObject(vValue);

            //Act
            var vResult = _controller.Update(vKey, vData);

            //Asset

            var vInvoice = _context.Invoices.First(i => i.InvId == vKey);
            Assert.That(vUpdatedStatus, Is.EqualTo(vInvoice.StatusId));
            Assert.That(vResult, Is.TypeOf<OkResult>());
        }
    }

Hi, I had the same problem with IQueryable. Here's a solution I came up with to set up and mock my context(instead of mocking my whole context I only mock the tables that i need for the controller that i want test), as well as how I proceeded afterward. I hope it helps with your issue.

Upvotes: 0

Related Questions