Navigate / search

Mocking DbContext and DbSet with Moq

I believe that during your work with unit tests with applications that are using database for data storage you will need to isolate layer that is responsible for providing data. In this example I will use Entity Framework as ORM.

Below you can find main elements of solution:

public class User
{
  public int Id { get; set; }
  public string Login { get; set; }
  public string Name { get; set; }
  public string Surname { get; set; }
  public bool AccountLocked { get; set; }
  public virtual List<Role> Roles { get; set; }
}

public class UsersContext : DbContext
{
  public virtual DbSet<User> Users { get; set; }
  public virtual DbSet<Role> Roles { get; set; }
}

public class UsersService
{
  private readonly UsersContext usersContext;
  
  public UsersService(UsersContext usersContext)
  {
    this.usersContext = usersContext;
  }
 
  public User AddUser(string login, string name, string surname)
  {
    var newUser = this.usersContext.Users.Add(
      new User
      {
        Login = login,
        Name = name,
        Surname = surname,
        AccountLocked = false
      });
  
    this.usersContext.SaveChanges();
    return newUser;
  }
 
  public IList<User> GetLockedUsers ()
  {
    return this.usersContext.Users.Where(x => x.AccountLocked).ToList();
  }
}

You have two possibilities to isolate UsersContext:

  • Add stub implementation for UsersContect class that will be used in unit test. It should be injected instead of UsersContext class in unit tests.
  • Mock UsersContext by using one of mocking frameworks – in this case Moq.

Of course it is hard to say which option is better. In case of first one you can prepare environment that will suite better to our test environment. But on the other hand you will need to spend some time for writing it and later you will need to maintain this code.

Mocking framework maybe will not suite as well as own implementation of our solution but on the other hand we will not pay cost of creation stub and future maintenance. What’s more you will be able to change behaviour of mocked object easily.

In this example I will use following tools:

Of course I will be testing methods from UsersService class.

In such cases you should consider two cases: LINQ is being used to access data (method GetLockedUsed) and LINQ is not being used to access data (method AddUser).

Method without LINQ

This case is easier to implement but I have some doubts about writing unit test for such cases. You can test AddUser in two ways:

  • Isolate method this.usersContext.Users.Add in such way that it will simulate default behaviour and return correct User class object. Then you need to test if returned object has correct values:
    [Theory, AutoData]
    public void AddUser_Invoke_UserCreated_v1(string expectedLogin, string expectedName, string expectedSurname)
    {
      // Arrange
      var userContextMock = new Mock<UsersContext>();
      userContextMock.Setup(x => x.Users.Add(It.IsAny<User>())).Returns((User u) => u);
     
      var usersService = new UsersService(userContextMock.Object);
     
      // Act
      var user = usersService.AddUser(expectedLogin, expectedName, expectedSurname);
     
      // Assert
      Assert.Equal(expectedLogin, user.Login);
      Assert.Equal(expectedName, user.Name);
      Assert.Equal(expectedSurname, user.Surname);
      Assert.False(user.AccountLocked);
     
      userContextMock.Verify(x => x.SaveChanges(), Times.Once);
    }
    
  • You can check returned value only partially – check if returned object is not null and verify if we executed method that will add user to context with correct parameters:
    [Theory, AutoData]
    public void AddUser_Invoke_UserCreated_v2(string expectedLogin, string expectedName, string expectedSurname)
    {
      // Arrange
      var userContextMock = new Mock<UsersContext>();
      var usersMock = new Mock<DbSet<User>>();
      usersMock.Setup(x => x.Add(It.IsAny<User>())).Returns((User u) => u);
      userContextMock.Setup(x => x.Users).Returns(usersMock.Object);
     
      var usersService = new UsersService(userContextMock.Object);
     
      // Act
      var user = usersService.AddUser(expectedLogin, expectedName, expectedSurname);
     
      // Assert
      Assert.NotNull(user);
      usersMock.Verify(
        x =>
          x.Add(
            It.Is<User>(
              u =>
                u.Login == expectedLogin && u.Name == expectedName && u.Surname == expectedSurname &&
                !u.AccountLocked)), Times.Once);
      userContextMock.Verify(x => x.SaveChanges(), Times.Once);
    }
    

Of course in both cases we are verifying whether SaveChanges method has been invoked.

Let’s come back to my doubts. They are related to situation that instead of verifying results of method in unit tests we are checking if some method has been executed. I always wonder if we are testing behaviour of method or if we are testing method’s implementation.

Sometimes in unit test we are testing only if some methods have been invoked. Please note that such tests will not find error caused by wrong methods’ execution order. Because of that I prefer test similar to the first one although that even there we are checking if SaveChanges method has been executed only once.

Method with LINQ

I prefer this kind of test because you are checking result of method. To execute LINQ query you need to create list with objects (in this case list of users) and then you need to connect this list with IQueryable<out T> interface implementation in DbSet class. You can achieve it with following code:

var users = new List<User>
  {
    lockedUser,
    fixture.Build<User>().With(u => u.AccountLocked, false).Create(),
    fixture.Build<User>().With(u => u.AccountLocked, false).Create()
  }.AsQueryable();
 
  var usersMock = new Mock<DbSet<User>>();
  usersMock.As<IQueryable<User>>().Setup(m => m.Provider).Returns(users.Provider);
  usersMock.As<IQueryable<User>>().Setup(m => m.Expression).Returns(users.Expression);
  usersMock.As<IQueryable<User>>().Setup(m => m.ElementType).Returns(users.ElementType);
  usersMock.As<IQueryable<User>>().Setup(m => m.GetEnumerator()).Returns(users.GetEnumerator());

You can see here how you can use AutoFixture for creating objects.

And whole test:

[Fact]
public void GetLockedUsers_Invoke_LockedUsers_v1()
{
  // Arrange
  var fixture = new Fixture();
  var lockedUser = fixture.Build<User>().With(u => u.AccountLocked, true).Create();
  var users = new List<User>
  {
    lockedUser,
    fixture.Build<User>().With(u => u.AccountLocked, false).Create(),
    fixture.Build<User>().With(u => u.AccountLocked, false).Create()
  }.AsQueryable();
 
  var usersMock = new Mock<DbSet<User>>();
  usersMock.As<IQueryable<User>>().Setup(m => m.Provider).Returns(users.Provider);
  usersMock.As<IQueryable<User>>().Setup(m => m.Expression).Returns(users.Expression);
  usersMock.As<IQueryable<User>>().Setup(m => m.ElementType).Returns(users.ElementType);
  usersMock.As<IQueryable<User>>().Setup(m => m.GetEnumerator()).Returns(users.GetEnumerator());
 
  var userContextMock = new Mock<UsersContext>();
  userContextMock.Setup(x => x.Users).Returns(usersMock.Object);
 
  var usersService = new UsersService(userContextMock.Object);
 
  // Act
  var lockedUsers = usersService.GetLockedUsers ();
 
  // Assert
  Assert.Equal(new List<User> {lockedUser}, lockedUsers);
}

Probably you noticed that same code will be used for creation other DbSet mock. Because of that I propose to extract code responsible for this action to separate method / class:

private static Mock<DbSet<T>> CreateDbSetMock<T>(IEnumerable<T> elements) where T : class
{
  var elementsAsQueryable = elements.AsQueryable();
  var dbSetMock = new Mock<DbSet<T>>();
 
  dbSetMock.As<IQueryable<T>>().Setup(m => m.Provider).Returns(elementsAsQueryable.Provider);
  dbSetMock.As<IQueryable<T>>().Setup(m => m.Expression).Returns(elementsAsQueryable.Expression);
  dbSetMock.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(elementsAsQueryable.ElementType);
  dbSetMock.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(elementsAsQueryable.GetEnumerator());
 
  return dbSetMock;
}

Then our method will be shorter and easier to read.

[Fact]
public void GetLockedUsers_Invoke_LockedUsers_v2()
{
  // Arrange
  var fixture = new Fixture();
  var lockedUser = fixture.Build<User>().With(u => u.AccountLocked, true).Create();
  IList<User> users = new List<User>
  {
    lockedUser,
    fixture.Build<User>().With(u => u.AccountLocked, false).Create(),
    fixture.Build<User>().With(u => u.AccountLocked, false).Create()
  };
 
  var usersMock = CreateDbSetMock(users);
 
  var userContextMock = new Mock<UsersContext>();
  userContextMock.Setup(x => x.Users).Returns(usersMock.Object);
 
  var usersService = new UsersService(userContextMock.Object);
 
  // Act
  var lockedUsers = usersService.GetLockedUsers ();
 
  // Assert
  Assert.Equal(new List<User> {lockedUser}, lockedUsers);
}

Comments

avatar
Mark
Reply

Hi Michal,
I appreciate your post, was trying it out on my project because I have a similar setup and was looking into unit test some of the user services that use the DbContext. On the method without LINQ, at least on the latest libraries, what you suggest does not compile. Specifically this line that performs an invalid cast:
usersMock.Setup(x => x.Add(It.IsAny())).Returns((User u) => u);
The mock set up using DbSet will make users be instance of Microsoft.EntityFramework.ChangeTracking.EntryEntitty, and the User is that of your project’s class “User”.
Just wanted to point that out

avatar
Michał Jankowski
Reply

Hi Mark,

Could you please provide more details. I updated every dll to the latest version:
-- EF v6.2
-- Moq v4.8.1
-- xUnit v2.3.1
-- AutoFixture v4.0.1

And everything is working. Maybe you thought about EF Core?

avatar
Ram Jane
Reply

Is there any example to test Update DbSet like Add?

avatar
Michał Jankowski
Reply

Please check section “Method without LINQ”. There is example of unit test for usersContext.Users.Add. I believe this is what you would like to see.

Leave a comment

name*

email* (not published)

website

.