Mocking Ef Dbcontext with Moq

Mocking EF DbContext with Moq

I managed to solve it by creating a FakeDbSet<T> class that implements IDbSet<T>

public class FakeDbSet<T> : IDbSet<T> where T : class
{
ObservableCollection<T> _data;
IQueryable _query;

public FakeDbSet()
{
_data = new ObservableCollection<T>();
_query = _data.AsQueryable();
}

public virtual T Find(params object[] keyValues)
{
throw new NotImplementedException("Derive from FakeDbSet<T> and override Find");
}

public T Add(T item)
{
_data.Add(item);
return item;
}

public T Remove(T item)
{
_data.Remove(item);
return item;
}

public T Attach(T item)
{
_data.Add(item);
return item;
}

public T Detach(T item)
{
_data.Remove(item);
return item;
}

public T Create()
{
return Activator.CreateInstance<T>();
}

public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
{
return Activator.CreateInstance<TDerivedEntity>();
}

public ObservableCollection<T> Local
{
get { return _data; }
}

Type IQueryable.ElementType
{
get { return _query.ElementType; }
}

System.Linq.Expressions.Expression IQueryable.Expression
{
get { return _query.Expression; }
}

IQueryProvider IQueryable.Provider
{
get { return _query.Provider; }
}

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}

IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _data.GetEnumerator();
}
}

Now my test looks like this:

[TestMethod]
public void TestGetAllUsers()
{
//Arrange
var mock = new Mock<IDbContext>();
mock.Setup(x => x.Set<User>())
.Returns(new FakeDbSet<User>
{
new User { ID = 1 }
});

UserService userService = new UserService(mock.Object);

// Act
var allUsers = userService.GetAllUsers();

// Assert
Assert.AreEqual(1, allUsers.Count());
}

Moq mocking EF DbContext

You cannot mock DbSet query functionality. This is explained in the docs:

Properly mocking DbSet query functionality is not possible, since queries are expressed via LINQ operators, which are static
extension method calls over IQueryable. As a result, when some
people talk about "mocking DbSet", what they really mean is that they
create a DbSet backed by an in-memory collection, and then evaluate
query operators against that collection in memory, just like a simple
IEnumerable. Rather than a mock, this is actually a sort of fake,
where the in-memory collection replaces the the real database.

Mocking EF core dbcontext and dbset

I see you are using EF core DbContext in your MovieRepository. So instead of using mock, Using EF Core InMemory database will be a great option for you. This will also reduce the complexity.

Write your GetAllTest() method as follows:

[Fact]
public void GetAllTest()
{
var options = new DbContextOptionsBuilder<MovieDbContext>()
.UseInMemoryDatabase(databaseName: "MovieListDatabase")
.Options;

// Insert seed data into the database using one instance of the context
using (var context = new MovieDbContext(options))
{
context.Movies.Add(new Movie {Id = 1, Title = "Movie 1", YearOfRelease = 2018, Genre = "Action"});
context.Movies.Add(new Movie {Id = 2, Title = "Movie 2", YearOfRelease = 2018, Genre = "Action"});
context.Movies.Add(new Movie {Id = 3, Title = "Movie 3", YearOfRelease = 2019, Genre = "Action"});
context.SaveChanges();
}

// Use a clean instance of the context to run the test
using (var context = new MovieDbContext(options))
{
MovieRepository movieRepository = new MovieRepository(context);
List<Movies> movies == movieRepository.GetAll();

Assert.Equal(3, movies.Count);
}
}

Note: Don't forget to install Microsoft.EntityFrameworkCore.InMemory nuget package as follows:

Install-Package Microsoft.EntityFrameworkCore.InMemory

For more details: Testing with InMemory

Mocking EF Core Database Context using Moq and xUnit?

Don't mock DbContext, because tests with mocking dbContext will not provide good quality feedback for developer.

Mocked DbContext will verify only that some method are called, which will convert code maintenance (refactoring) into a nightmare.

Instead use In-Memory provider or Sqlite provider with "in-memory" feature.

EF Core In-Memory Database Provider

Using SQLite to test an EF Core application

Alternative approach is to test against actual database - such tests will provide more confidence that application works correctly in "production" environment.

Moq testing Entity Framework

A key reason for implementing a repository pattern with Entity Framework is to enable unit testing. The Repository becomes the abstraction point for the unit tests. Basically think of it this way:

  • You trust that EF is written and tested correctly to guarantee you'll get what you're supposed to get.
  • The repository's purpose is solely to serve as a relay, containing no high-level business logic.
  • Any low-level rules that a repository may enforce that are dependent on Data State will be covered by integration tests. These tests would be run using either a known-state snapshot database or in-memory database.

So where your main business logic should be residing in your Services / Controllers, that is where your unit tests would be targeting. The Mocks are made for your Repositories, not EF DbContexts/DbSets.

So for example, given your structure I would have a Mock of the UnitOfWork which asserts whether any Complete() call is made (or not), then mock out the Repository for expected methods to return a known state.

Mock an Entity DbContext Insert

the problem is you are mocking the object but no the methods for the object.
it would be something like this :

mockContext.Setup(p => p.Set<Table1>().Add(It.IsAny<Table1>())).Returns(_table1);

that way the mock will create an instance of the dbSet and will return what ever you want

Error Trying to Mock DbContext EF Core .NET 6 Visual Studio 2022

The problem is that you're assigning an object of type Mock to the variable mockDbContext. The UserStoryRepository is expecting an object of type UserStoryContext in the constructor.

You need to change the construction of the repository to:

Repository = new UserStoryRepository(mockDbContext.Object);

Unit testing using EF Core InMemoryDatabase along with Moq mocks

I've got an answer from one of NUnit collaborator under this thread:
https://github.com/nunit/nunit/issues/4090

Long story short - make sure that you are creating new DbContext instance each time when it's needed and just reuse the "options" to point out on which InMemory database should they operate:

private static readonly object[] _safeRemoveSource = 
{
new TestCaseData("Foo1", 1, new List<Foo>()).SetName("SafeRemove_FooExists_FooRemoved"),
new TestCaseData("Foo2", 1, new List<Foo> { new Foo { Name = "Foo1", BarId = 1 } }).SetName("SafeRemove_FooWithDifferentNameExists_FooNotRemoved"),
new TestCaseData("Foo1", 2, new List<Foo> { new Foo { Name = "Foo1", BarId = 1 } }).SetName("SafeRemove_FooWithDifferentBarIdExists_FooNotRemoved"),
}

[TestCaseSource(nameof(_safeRemoveSource))]
public void SafeRemoveTest(string name, int barId, IList<Foo> expectedFoos)
{
var initialFoos = new List<Foo> { new Foo { Name = "Foo1", BarId = 1 } };
var options = new DbContextOptionsBuilder<MyDbContext>().UseInMemoryDatabase(Guid.NewGuid().ToString()).Options;

using var context = new TestMyDbContext(options);
context.Foos.AddRange(actualFoos);
context.SaveChanges();

var contextFactoryMock = new Mock<IDbContextFactory<MyDbContext>>();
contextFactoryMock.Setup(factory => factory.CreateDbContext()).Returns(new TestMyDbContext(options));

var safeRemover = new SafeRemover(contextFactoryMock.Object);

safeRemover.SafeRemove(name, barId);
var actualFoos = new TestMyDbContext(options).Foos.ToList();

Assert.AreEqual(expectedFoos.Count(), actualFoos.Count());
for (var i = 0; i < expectedFoos.Count(); i++)
Assert.That(expectedFoos[i].Name.Equals(actualFoos[i].Name) && expectedFoos[i].BarId == actualFoos[i].BarId);
}

How to mock DbContext in Entity Framework Core 2.0 for raw queries ExecuteSqlCommand

If testing raw query then you will need to execute it against an actual database in an integration test.

Using a mock will only fake a response, which will be a misleading test.

Configure the desired service so that it can be tested.

public async Task checks_should_return_ok() {
//Arrange
var configuration = new ConfigurationBuilder()
.SetBasePath("set to output path of test project")
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.Build(); //also make sure appsetting.json is copied to output.

var serviceCollection = new ServiceCollection();

serviceCollection.AddDbContext<MyDbContext >(options =>
options.UseSqlServer(configuration.GetConnectionString("name here"))
);

serviceCollection.AddTransient<DBCheckDataService>();

var services = serviceCollection.BuildServiceProvider();

var subject = services.GetRequiredService<DBCheckDataService>();
var expected = true;

//Act
var actual = await subject.PerformHealthChecks();

//Assert
Assert.AreEqual(expected, actual);
}

In the above example, the connection string is loaded from the appsettings.json, and DI is used to configure the creation of the subject undertest.



Related Topics



Leave a reply



Submit