In my project ELP I decided to start with core implementation of backend side. I started with service for sign in and register user accounts. I use TDD approach, so I create test with expected result that fails at the beginning and then I implement code to pass this test.
I created MembershipService in my project that is responsible for creating accounts, validating, etc. This service uses other services like UserService for getting users from database or UserRoleService for getting user roles. Services use context, (BTW I rejected using repository pattern, maybe I will describe more about it in the future) and context contains DbSets.
1 2 3 4 5 6 7 8 9 10 11 |
public class UserService : EntityService<User>, IUserService { public UserService(IContext context) : base(context) { } public User GetUserByUsername(string username) { return _dbset.FirstOrDefault(u => u.Username == username); } } |
First problem that you can meet is how to mock this thing?
Let’s see how to write a test for method GetUserByUsername.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[Fact] public void GetUserByUsernameTest() { string username = "testUser1"; var ctx = new Mock<IContext>(); List<User> users = new List<User>(); users.Add(new User() { Username = username}); var mockDbSet = ServiceTestsHelper.GetMockDbSet<User>(users); ctx.Setup(c => c.Set<User>()).Returns(mockDbSet.Object); IUserService service = new UserService(ctx.Object); var result = service.GetUserByUsername(username); Assert.NotNull(result); } |
This is my test. I use xUnit testing framework and Moq for mocking. If you want to use it as I, you need to install it from Nuget Packages manager.
As you see I created list with one user and I want to pass it to my context, It will act like data get from database. First we need to create our DbSet for Users. I have little helper method for it. It is generic, so you can use it too.
1 2 3 4 5 6 7 8 9 10 |
internal static Mock<DbSet<T>> GetMockDbSet<T>(ICollection<T> entities) where T : class { var mockSet = new Mock<DbSet<T>>(); mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(entities.AsQueryable().Provider); mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(entities.AsQueryable().Expression); mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(entities.AsQueryable().ElementType); mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(entities.AsQueryable().GetEnumerator()); mockSet.Setup(m => m.Add(It.IsAny<T>())).Callback<T>(entities.Add); return mockSet; } |
Ok, so first test we have completed, but there’s one more thing that I wanted to share with you.
Let’s get back to my service that I mentioned before – MembershipService. I’d like to test CreateUser method which at the end adds user entity to User’s DbSet. Problem is with verifying it if you use mocks. If you check User’s DbSet from Context class there’s nothing in it. Null. And here comes Verify method from Moq framework that helps.
Here are 3 lines of code from my testing method, it executes CreateUserMethod, so I expect User’s DbSet will contain one item and UserRole’s DbSet will contain two items.
1 2 3 |
User result = service.CreateUser("testUser1", "test@email.com", "password", new List<int>() { 1, 2 }); usersMockDbSet.Verify(m => m.Add(It.IsAny<User>()), Times.Once); userRolesMockDbSet.Verify(m => m.Add(It.IsAny<UserRole>()), Times.Exactly(2)); |
With help of Verify method result is as expected and test is green.
If you want to read more about testing Entity Framework you can go to https://msdn.microsoft.com/en-us/library/dn314429(v=vs.113).aspx
There is a easiest way 🙂
http://www.22bugs.co/post/Mocking-DbContext/
Pros:
– someone did it 😀
– less code for many different collections
– you can do almost everything like on real DBConext. I know from my experience, that there was only one problem I was facing, how to disable proxy creation to get POCO
Thanks! I will check it 😉