0

I am trying to test .net core 2.2 api's. I am having difficulty mocking (using moq) the dbcontext. What is the syntax for adapting my mockDbContext to be usable. I receive a NullReferenceException. Since the Changetracker is never instantiated I believe. Do I need a different approach? I saw mentioned .UseInMemoryDatabase() but, with very little documentation or good examples.

Below is the code I am trying to use in my test [Fact].

     var mockDbContext = new Mock<dbContext>(optionsBuilder.Options);
     var controller = new HomeController(mockDbContext.object);

Then use controller to test... removed for brevity

var datafromdbcontext = controller.GetData();

Below is an example of my dbcontext.

    public class dbContext:DbContext
    {
        public dbContext(DbContextOptions<dbContext> options)
        : base(options)
        {
            //MAKE IT READONLY
            ChangeTracker.QueryTrackingBehavior = 
       QueryTrackingBehavior.NoTracking;
        }

3 Answers 3

2

There are lots of examples of using the InMemory database (which is what you normally use for unit tests) ... Here is an example: https://learn.microsoft.com/en-us/ef/core/miscellaneous/testing/in-memory

You shouldn't try to Mock the actual context, you use the InMemory option instead. So - your context, but with the InMemory "option"... Like so:

var myFakeContext = new DbContextOptionsBuilder<MYDBCONTEXT>().UseInMemoryDatabase("SO-MADE-UP-NAME"); 
Sign up to request clarification or add additional context in comments.

13 Comments

So I should be using .InMemoryDatabase() if I am having moq Issues?
Yes, in fact in EF Core you shouldn't mock your DbContext at all but opt for InMemoryDatabase
@Sangman beat me to it.. Yes, so, you shouldnt try to Mock the actual context, you use the InMemory option instead. So - your context, but with the InMemory "option"... I've added the example to the answer
Yes, use InMemoryDatabase and see how far it gets you. There are a few things in-memory doesn't support. Should you need to mock a FromSql (eg, calling a stored proc), check out my example here: github.com/efarr/mock-ef-core-fromsql
@Fabio - It depends totally on what you're trying to test and what you're trying to achieve. Theres two different types of tests designed for different reasons.The one you're referring to is called an integration test and its fundamentally different to a unit test. Mocking typically (but not necessarily always) comes into play in "Unit Tests".
|
0

I have created a library, available on NuGet and GitHub called EntityFrameworkCoreMock that does the heavy lifting of setting everything up. There's also an implementation for Moq.

https://github.com/huysentruitw/entity-framework-core-mock

Example usage

public class User
{
    [Key, Column(Order = 0)]
    public Guid Id { get; set; }

    public string FullName { get; set; }
}

public class TestDbContext : DbContext
{
    public TestDbContext(DbContextOptions<TestDbContext> options)
        : base(options)
    {
    }

    public virtual DbSet<User> Users { get; set; }
}

public class MyTests
{
    [Fact]
    public void Test()
    {
        var initialEntities = new[]
        {
            new User { Id = Guid.NewGuid(), FullName = "Eric Cartoon" },
            new User { Id = Guid.NewGuid(), FullName = "Billy Jewel" },
        };

        var dbContextMock = new DbContextMock<TestDbContext>(DummyOptions);
        var usersDbSetMock = dbContextMock.CreateDbSetMock(x => x.Users, initialEntities);

        // Pass dbContextMock.Object to the class/method you want to test

        // Query dbContextMock.Object.Users to see if certain users were added or removed
        // or use Mock Verify functionality to verify if certain methods were called: usersDbSetMock.Verify(x => x.Add(...), Times.Once);
   }

    public DbContextOptions<TestDbContext> DummyOptions { get; } = new DbContextOptionsBuilder<TestDbContext>().Options;
}

Comments

0

Actually you can Mock DbContext like this :

source (Synchronous/Asynchronous): https://learn.microsoft.com/fr-fr/ef/ef6/fundamentals/testing/mocking?redirectedfrom=MSDN

            var mockSet = new Mock<DbSet<User>>();
            mockSet.As<IDbAsyncEnumerable<User>>()
              .Setup(m => m.GetAsyncEnumerator())
              .Returns(new TestDbAsyncEnumerator<User>(data.GetEnumerator()));

            mockSet.As<IQueryable<User>>()
               .Setup(m => m.Provider)
               .Returns(new TestDbAsyncQueryProvider<User>(data.Provider));

            mockSet.As<IQueryable<User>>().Setup(m => m.Expression).Returns(data.Expression);
            mockSet.As<IQueryable<User>>().Setup(m => m.ElementType).Returns(data.ElementType);
            mockSet.As<IQueryable<User>>().Setup(m => m.GetEnumerator()).Returns(() => data.GetEnumerator());

            var mockContext = new Mock<IWebApiDbContext>();
            mockContext.Setup(m => m.User).ReturnsDbSet(mockSet.Object);

But you will cannot alterate(Add/Delete/Update) the data. Only Get them.

so the solution can be Use in Memory :

var options = new DbContextOptionsBuilder<WebApiDbContext>()
                .UseInMemoryDatabase(databaseName: "dbContext")
                .Options;

            var context = new WebApiDbContext(options);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.