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
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); }
DbSet mock, no results while calling usersMock.Object.ToList() secondly
Problem:
dbSetMock.As<IQueryable>().Setup(m => m.GetEnumerator()).Returns(elementsAsQueryable.GetEnumerator());
Solution:
dbSetMock.As<IQueryable>().Setup(m => m.GetEnumerator()).Returns(() => elementsAsQueryable.GetEnumerator());
-- Return a function that returns the enumerator, that will return a new enumerator each time you ask for it.
need to write a xunit test for “dbContext.Users.Where”, I tried the above you mentioned but i getting the below exception for dbcontext.setup ()
System.NotSupportedException : Unsupported expression: x => x.Users
Non-overridable members (here: dbContext.get_Users) may not be used in setup / verification expressions.
Could you please help me this?
Probably Users are not marked as virtual.
var userContextMock = new Mock();
userContextMock.Setup(x => x.Users.Add(It.IsAny())).Returns((User u) => u);
throws:
Cannot implicitly convert type ‘app.Models.User’ to ‘Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry’
Any idea how to fix it?
It depends on what you want to do.
One of the solutions can be as follow:
();())).Callback(
// Arrange
var userContextMock = new Mock
User user = null;
userContextMock.Setup(x => x.Users.Add(It.IsAny
(User u) =>
{
user = u;
});
var usersService = new UsersService(userContextMock.Object);
// Act
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);
Hi Michal,
This blog was really useful for me. Thank you very much for writing it in a simple way. I was testing the following method
public List GetAllAlarmByCustomer(CurrentUser user)
{
var alarmMessageList = _dbContext.AlarmMessages.Where(m => m.FkCustomerId == user.CustomerId);
return alarmMessageList.ToList();
}
And I developed my unit test using “Method with LINQ”. I am getting the following error ,
System.ArgumentException : The expression’s Body is not a MemberExpression. Most likely this is because it does not represent access to a property or field. (Parameter ‘propertyPicker’)
And the line of code is,
var local= fixture.Build().With(u => (u.FkCustomerId == user.CustomerId), true).Create();
Could you please help me to find the problem with my code.?
Thanks in advance,
Sajina
Hi Sajina,
Mentioned line of code looks like AutoFixture and you are adding there some kind of logic expression. And you should use With to assign value to property in the generated object.
Can you please share your sample project?
You can find a sample project on GitHub repository
Good writeup!!
Beware that using LINQ multiple times on your mocked DbSet may end up yielding no results. To fix this, pass the mocked call to GetEnumerator() as a lambda, as seen here: https://stackoverflow.com/a/33528122
Will be improved in next version.
Take your attention on such importan thing like GetEnumerator which should called as Func here:
mockSet.As().Setup(m => m.GetEnumerator()).Returns( ( )=> data.GetEnumerator());
instead of your wrong implemenration
mockSet.As().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
Because Returns(data.GetEnumerator()); will reach end of collection after first loop and each next request to collection\mocked object will return empty result!
Good suggestion!
I found this tutorial very useful. I modified it slightly for my purposes, and to also use an interface to decouple my dbcontext derived class, from my equivalent of the service class. I’m not using XUnit or AutoFixture, but I am using NUnit and Moq, so the code is fairly close. Thanks for posting this!
Hi Michal,
I am facing a issue in my Unit test project, I am getting following issue mention in below link, Please help to troubleshoot the issue if you can.
https://social.msdn.microsoft.com/Forums/en-US/391a31ae-6e4d-454c-8852-e7266e982370/unit-test-project-issue?forum=adodotnetentityframework
Could you please provide an example of your problem as VS project zipped to one file. And describe what you would like to achieve.
Super wpis!
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
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?
Is there any example to test Update DbSet like Add?
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.
[…] wywołania synchroniczne – Mockowanie typów DbContext oraz DbSet z wykorzystaniem Moq […]
[…] czas temu opisałem w jaki sposób można zamockować typy DbContext przy pomocy Moq -- Mockowanie typów DbContext oraz DbSet z wykorzystaniem Moq. Temat ten nie został wtedy całkowicie wyczerpany. Pozostał jeden element do opisania – […]
Akurat dzisiaj miałem ten sam problem -- jak mockować DbContext?.
Ze wszystkich możliwych rozwiązań wybrałem Effort -- in-memory database.
https://effort.codeplex.com/