Trying to write unit tests that run against DbContext, in .Net Core. I have performed the same in full framework EF and all works.
The code below represents the minimum code to recreate the issue. I have stripped out anything that isn't implemented
I have the following mocks
// DbContext
public class MockDbContext : DbContext
{
public virtual DbSet<State> State { get; set; }
}
The Repository
// Mock of my repository
public class MockRepository<TModel> : IRepository<TModel> where TModel : class
{
MockDbContext _context;
DbSet<TModel> _dbSet;
public MockRepository(MockDbContext context)
{
_context = context;
// THIS IS NULL
_dbSet = _context.Set<TModel>();
}
public async Task<IList<TModel>> GetAllAsync()
{
return _dbSet.ToListAsync().Result;
}
public void Add(TModel t)
{
_context.Add(t);
}
}
The Unit of work
// Unit of work
public class MockUnitOfWork : IUnitOfWork
{
private Mock<MockDbContext> _mockDbContext;
private IRepository<State> _stateRepo;
public MockUnitOfWork()
{
var mockState = new List<State>() { new State { StateId = 1, Name = "Added" }, new State { StateId = 1, Name = "Deleted" } }.AsQueryable();
var mockStateSet = new Mock<DbSet<State>>();
mockStateSet.As<IQueryable<State>>().Setup(m => m.Provider).Returns(mockState.Provider);
mockStateSet.As<IQueryable<State>>().Setup(m => m.Expression).Returns(mockState.Expression);
mockStateSet.As<IQueryable<State>>().Setup(m => m.ElementType).Returns(mockState.ElementType);
mockStateSet.As<IQueryable<State>>().Setup(m => m.GetEnumerator()).Returns(mockState.GetEnumerator());
_mockDbContext = new Mock<MockDbContext>();
_mockDbContext.Setup(o => o.State).Returns(mockStateSet.Object);
}
public void SaveAsync()
{
_mockDbContext.Object.SaveChanges();
}
public IRepository<State> StateRepository => _stateRepo ?? (_stateRepo = new MockRepository<State>(_mockDbContext.Object));
}
I'm attempting to test some service code, which isn't included here, though the problem can be recreated by calling the following, from a test method.
MockUnitOfWork unitOfWork = new MockUnitOfWork();
var x = unitOfWork.StateRepository.GetAllAsync().Result;
We also have an extension that implements pretty much what can be found Entity Framework Testing with a Mocking Framework under the testing async section. This also results in a null on the DbSet<>
Any idea what i might be missing?
When using Entity Framework Core, the Microsoft.EntityFrameworkCore.InMemory NuGet package provides the infrastructure required for testing scenarios. Your repository can then use an appropriate DbContext, and your unit of work can accept an appropriate repository.
Additional details about testing with the InMemory provider in EF Core can be found at https://learn.microsoft.com/en-us/ef/core/miscellaneous/testing/in-memory.
Related
I want to know how to use Moq for mocking my EF Core's DbContext when I am using DI to provide my Database context to the controller as shown below:
public class RegisterController : Controller
{
private AppDbContext context;
public RegisterController(AppDbContext appDbContext)
{
context = appDbContext;
}
public IActionResult Create()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Create(Register register)
{
if (ModelState.IsValid)
{
context.Add(register);
await context.SaveChangesAsync();
return RedirectToAction("Read");
}
else
return View();
}
}
Here AppDbContext is my database context for the EF core.
I want to write test case for the Create action. I have tried the below code:
[Fact]
public async Task Test_Create_POST_ValidModelState()
{
// Arrange
var r = new Register()
{
Id = 4,
Name = "Test Four",
Age = 59
};
var mockRepo = new Mock<AppDbContext>();
mockRepo.Setup(repo => repo.CreateAsync(It.IsAny<Register>()))
.Returns(Task.CompletedTask)
.Verifiable();
var controller = new RegisterController(mockRepo.Object);
// Act
var result = await controller.Create(r);
// Assert
var redirectToActionResult = Assert.IsType<RedirectToActionResult>(result);
Assert.Null(redirectToActionResult.ControllerName);
Assert.Equal("Read", redirectToActionResult.ActionName);
mockRepo.Verify();
}
The main problem here is that I cannot do:
var mockRepo = new Mock<AppDbContext>();
I want to follow this approach as I have already added in-memory database.
Note that I know there is a another way to test with repository pattern. Which can be done by changing the create action as:
public class RegisterController : Controller
{
private IRegisterRepository context;
public RegisterController(IRegisterRepository appDbContext)
{
context = appDbContext;
}
public IActionResult Create()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Create(Register register)
{
if (ModelState.IsValid)
{
await context.CreateAsync(register);
return RedirectToAction("Read");
}
else
return View();
}
}
Where the interface IRegisterRepository and RegisterRepository.cs codes are:
public interface IRegisterRepository
{
Task CreateAsync(Register register);
}
public class RegisterRepository : IRegisterRepository
{
private readonly AppDbContext context;
public RegisterRepository(AppDbContext dbContext)
{
context = dbContext;
}
public Task CreateAsync(Register register)
{
context.Register.Add(register);
return context.SaveChangesAsync();
}
}
and the startup.cs code which add it as a service is:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>(optionsBuilder => optionsBuilder.UseInMemoryDatabase("InMemoryDb"));
services.AddScoped<IRegisterRepository, RegisterRepository>();
services.AddControllersWithViews();
}
I do not want to follow this approach (the repository pattern). I want to fake database context directly because I want to directly do the insertion of the record in the create action as the controller is getting database context object in it's constructor.
So how to write fake database context using moq in this test methods here?
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.
There are several really useful libraries which does support what you are looking for.
Here are two of my favourite ones:
EntityFrameworkCore3Mock
Github repo
The only prerequisite is that you have to define your DbSet as virtual.
public class AppDbContext: DbContext
{
public virtual DbSet<Entity> Entities { get; set; }
}
Then the mocking would be this easy:
var initialEntities = new[]
{
new Entity { ... },
new Entity { ... },
};
var dbContextMock = new DbContextMock<AppDbContext>(DummyOptions);
var usersDbSetMock = dbContextMock.CreateDbSetMock(x => x.Entities, initialEntities);
EntityFrameworkCore.Testing
Github repo
The only difference here is how you initialize the table with data:
var initialEntities = new[]
{
new Entity { ... },
new Entity { ... },
};
var dbContextMock = Create.MockedDbContextFor<AppDbContext>();
dbContextMock.Set<Entity>().AddRange(initialEntities);
dbContextMock.SaveChanges();
Any better examples or tutorials available on Unit testing projects using Entity framework than this
http://www.asp.net/web-api/overview/testing-and-debugging/mocking-entity-framework-when-unit-testing-aspnet-web-api-2
In my case API project is using Entity framework file Edmx file and accessing the tables from the edmx file from Repository class. [ Not really like the codefirst or dbfirst approach ]
The structre of repo class looks like
public class AppBackendRepository
{
// modify the type of the db field
private AppDBEntities db_context = new AppDBEntities();
public List<Student> Get()
{
return db_context.Students.ToList();
}
}
public class StudentController
{
private static AppBackendRepository repo;
public StudentController()
{
repo = new AppBackendRepository();
}
public IEnumerable<Student> GetStudents()
{
List<Student> students = repo.Get();
return students;
}
}
How can i write a proper Unit testing against this way of code architecture
The quick answer is: You don't.
Now, I say this because I tend to regard "Unit Tests" as something that is quick and can be used in continuous integration, while "Integration tests" are the slow tests that only run at night and, of course, when you're working with them.
The problem you're creating here is that you're using untestable code.
Take your method, "GetStudents()" as an example. You're depending on the repo to actually exist before calling this method. Any unit-test will depend on Entity Framework being installed, AND of course, this will be super-slow. Imagine a few hundred of these, and your unit test framework is now a serious clog in your system that makes people say "IT's so slow that we don't use it"
A better approach would be to implement the Dependency Inversion Principle
First, define an interface:
public interface IStudentRepository
{
IEnumerable<Student> GetStudents();
}
Now, your class is just an implementation detail of that contract, for example:
public class StudentRepository : DbContext, IStudentRepository
{
private DbSet<Student> Students;
public IEnumerable<Student> GetStudents()
{
return Students;
}
}
In the class that uses your repository, you can now inject your instance by constructor injection, and end up with something that is fully unit-testable:
public class StudentEnrollment
{
private readonly IStudentRepository _studentRepository;
// Inject the contract here
public StudentEnrollment(IStudentRepository studentRepository)
{
_studentRepository = studentRepository;
}
public IEnumerable<Student> GetStudentsForClass(StudentClass studentClass)
{
return _studentRepository.GetStudents().Where(student => student.class == studentClass);
}
}
And now, as the added bonus, you can Unit-Test every last bit of logic, for example:
[TestMethod]
public void GetStudentsForClass_GetStudentsThrowsException_ResultIsNull()
{
// Arrange
var mock = Mock.Create<IStudentRepository();
var badException = new Exception("I'm bad");
mock.Setup(repo => repo.GetStudents()).Throws(badException);
var someClass = new StudentClass();
var instance = new StudentEnrollment(mock.object);
// Act
var result = instance.GetStudentsForClass(studentClass);
// Assert
result.ShouldBeEmpty();
}
I'm of the opinon that all your code should be tested. This way you can easily detect when some developer breaks an expected chain. Because of that I always add tests for both repositories and controllers. In your case I would add a test that ensures that your controller uses your repository in a correct way, and that your repository uses EF the right way. However, you should not test EF itself. That's Microsofts problem.
First you must abstract the DbContext.
public class YourContext : DbContext, IDbContext
{
public virtual IDbSet<Student> Students { get; set; }
}
public interface IDbContext
{
IDbSet<Student> Students;
}
// Util for creating a testable context.
public class ContextUtils
{
internal static IDbSet<T> GetMockDbSet<T>(IEnumerable<T> data) where T : class
{
IQueryable<T> queryable = data.AsQueryable();
IDbSet<T> dbSet = MockRepository.GenerateMock<IDbSet<T>, IQueryable>();
dbSet.Stub(m => m.Provider).Return(queryable.Provider);
dbSet.Stub(m => m.Expression).Return(queryable.Expression);
dbSet.Stub(m => m.ElementType).Return(queryable.ElementType);
dbSet.Stub(m => m.GetEnumerator()).Return(queryable.GetEnumerator());
return dbSet;
}
public static IDbContext GetMockDbContext()
{
var dbContext = MockRepository.GenerateMock<IDbContext>();
dbContext.Stub(x => x.Student).PropertyBehavior();
dbContext.Students = GetMockDbSet(GetStudents());
return dbContext;
}
private static IEnumerable<Student> GetStudents()
{
// Create some mock data.
return new List<Student>
{
new Student()
{
StudentID = 1,
Name = "Student One",
},
new Student()
{
StudentID = 2,
Name = "Student Two",
},
new Student()
{
StudentID = 3,
Name = "Student Three",
}
};
}
}
Now you have a DbContext that can be tested. More information regarding the mocking of DbContext can be found on this blog.
http://aikmeng.com/post/62817541825/how-to-mock-dbcontext-and-dbset-with-moq-for-unit
Then make sure that you can test your repository.
public class AppBackendRepository
{
private IDbContext _dbContext;
// With injection.
public AppBackendRepository(IDbContext context)
{
_dbContext = context;
}
public List<Student> Get()
{
return _dbContext.Students.ToList();
}
}
It can also be done with a factory.
public class AppBackendRepository
{
public List<Student> Get()
{
using (var context = DbContextFactory.GenerateContext())
{
return context .Students.ToList();
}
}
}
public interface IDbContextFactory
{
/// <summary>
/// Creates a new context.
/// </summary>
/// <returns></returns>
IDbContext GenerateContext();
/// <summary>
/// Returns the previously created context.
/// </summary>
/// <returns></returns>
IDbContext GetCurrentContext();
}
public class DbContextFactory : IDbContextFactory
{
private IDbContext _context;
public IDbContext GenerateContext()
{
_context = new DbContext();
return _context;
}
public IDbContext GetCurrentContext()
{
if (_context == null)
_context = GenerateContext();
return _context;
}
}
Now you can test the repository and make sure that it's using EF the right way.
[TestMethod]
public void ShouldReturnAllValues()
{
int correctAmount = 3; // The number specified in MockUtils.
var dbContext = MockUtils.GetMockDbSet();
var repo = new AppBackendRepository(dbContext);
var result = repo.Get();
Assert.IsTrue(result.Count() == correctAmount);
}
What you actually tested is that no developer broke the intended code with something like:
public class AppBackendRepository
{
private IDbContext _dbContext;
// With injection.
public AppBackendRepository(IDbContext context)
{
_dbContext = context;
}
public List<Student> Get()
{
// Only active...
return _dbContext.Students.Where(x => x.Active).ToList();
}
}
Now that you know that the repo is doing what it's supposed to, you can simply make sure that your controller is calling the repo and actually returns the value.
public class StudentController
{
private static IAppBackendRepository _repo;
public StudentController(IAppBackendRepository repo)
{
_repo = repo;
}
public IEnumerable<Student> GetStudents()
{
List<Student> students = _repo.Get();
return students;
}
}
[TestMethod]
public void ShouldCallRepo()
{
// With Rhino
var mockRepo = MockRepository.GenerateStub<IAppBackendRepository>();
var expectedResult = new List<Student>();
mockRepo.Expect(x => x.Get()).Return(expectedResult);
var controller = new StudentController(mockRepo);
var actualResult = controller.GetStudents();
mockRepo.VerifyAllExpectations();
Assert.AreEqual(actualResult, expectedResult); // Possible in it's own method.
}
What you actually tested here is that your controller doesn't manipulate the list before returning it, and that it's actually using the repo as intended.
Also, you might consider using an IoC like Structuremap or Unity. It makes it much easier to make testable applications.
I have the following classes (where PilsnerContext is a DbContext class):
public abstract class ServiceBase<T> : IService<T> where T: class, IEntity
{
protected readonly PilsnerContext Context;
protected ServiceBase(PilsnerContext context)
{
Context = context;
}
public virtual T Add(T entity)
{
var newEntity = Context.Set<T>().Add(entity);
Context.SaveChanges();
return newEntity;
}
}
public class ProspectsService : ServiceBase<Prospect>
{
public ProspectsService(PilsnerContext context) : base(context){}
}
And i'm trying to make a unit test of the Add method mocking the context like:
[TestClass]
public class ProspectTest
{
[TestMethod]
public void AddProspect()
{
var mockProspect = new Mock<DbSet<Prospect>>();
var mockContext = new Mock<PilsnerContext>();
mockContext.Setup(m => m.Prospects).Returns(mockProspect.Object);
var prospectService = new ProspectsService(mockContext.Object);
var newProspect = new Prospect()
{
CreatedOn = DateTimeOffset.Now,
Browser = "IE",
Number = "1234567890",
Visits = 0,
LastVisitedOn = DateTimeOffset.Now
};
prospectService.Add(newProspect);
mockProspect.Verify(m=>m.Add(It.IsAny<Prospect>()), Times.Once);
mockContext.Verify(m=>m.SaveChanges(), Times.Once);
}
}
But the assert:
mockProspect.Verify(m=>m.Add(It.IsAny<Prospect>()), Times.Once);
Is failing, I assume is because I'm using Context.set().Add() instead of Context.Prospects.Add() in the Add method but how is the correct way to pass this test?
The exception is:
Expected invocation on the mock once, but was 0 times: m => m.Add(It.IsAny<Prospect>()) No setups configured. No invocations performed.
Thanks in advance.
It looks like you're just missing the setup to return your DbSet:
mockContext.Setup(m => m.Set<Prospect>()).Returns(mockProspect.Object);
I tried your solution Patrick Quirk but I was getting an error telling me that DbContext.Set is not virtual.
I found the solution for that here:
How to mock Entity Framework 6 Async methods?
Creating an interface of the DbContext like
public interface IPilsnerContext
{
DbSet<T> Set<T>() where T : class;
}
That way I could mock it.
Thanks!
This is my first question btw, I'm not sure if I can mark this question as duplicated or something.
I have a custom membership provider which connects to a user repository like this:
public class MyMembershipProvider : MembershipProvider {
[Inject]
public IUserRepository UserRepository { get; set; }
...
//Required membership methods
}
I am using ninject for my DI. Now I would like to test the provider, and have a mock user repository injected to allow me to do this. So something like:
...
IList<User> users = new List<User> {
new User { Email="matt#test.com",
UserName="matt#test.com",
Password="test"
}
};
var mock = new Mock<IUserRepository>();
mock.Setup(mr => mr.FindByUsername(
It.IsAny<string>())).Returns((string s) => users.Where(
x => x.UserName.Equals(s,StringComparison.OrdinalIgnoreCase)).Single());
...
And here is where I am not certain how to proceed, how do I get my mocked repository injected into my provider so that when a unit test that makes calls to the provider uses this mock repository?
Am I asking the right questions here?
EDIT - My final solution
For what it is worth I moved away from using mock to using an InMemory repository to maintain state so the provider would properly test certain functions. Right now I am only using this to test things like my provider. I ended up with:
generic InMemoryRepository:
class InMemoryRepository<TEntity> : IRepository<TEntity> where TEntity : class {
private int _incrementer = 0;
public Dictionary<int, TEntity> List = new Dictionary<int, TEntity>();
public string IDPropertyName {get; set;}
public void Create(TEntity entity) {
_incrementer++;
entity.GetType().GetProperties().First(p => p.Name == IDPropertyName).SetValue(entity, _incrementer, null);
List.Add(_incrementer,entity);
}
public TEntity GetById(int id) {
return List[id];
}
public void Delete(TEntity entity) {
var key = (int)entity.GetType().GetProperties().First(p => p.Name == IDPropertyName).GetValue(entity, null);
List.Remove(key);
}
public void Update(TEntity entity) {
var key = (int)entity.GetType().GetProperties().First(p => p.Name == IDPropertyName).GetValue(entity, null);
List[key] = entity;
}
}
Then my actual user repository - I do not have generic ID fields which is why I am using the IDPropertyName variable:
class InMemoryUserRepository : InMemoryRepository<User>, IUserRepository {
public InMemoryUserRepository() {
this.IDPropertyName = "UserID";
}
public IQueryable<User> Users {
get { return List.Select(x => x.Value).AsQueryable(); }
}
public User FindByUsername(string username) {
int key = List.SingleOrDefault(x=>x.Value.UserName.Equals(username, StringComparison.OrdinalIgnoreCase)).Key;
return List[key];
}
}
My membership test base class:
[TestClass]
public class MyMembershipProviderTestsBaseClass : IntegrationTestsBase {
protected MyMembershipProvider _provider;
protected NameValueCollection _config;
protected MembershipCreateStatus _status = new MembershipCreateStatus();
[TestInitialize]
public override void Initialize() {
base.Initialize();
// setup the membership provider
_provider = new MyMembershipProvider();
MembershipSection section = (MembershipSection) ConfigurationManager.GetSection("system.web/membership");
NameValueCollection collection = section.Providers["MyMembershipProvider"].Parameters;
_provider.Initialize(null, collection);
_status = new MembershipCreateStatus();
}
[TestCleanup]
public override void TestCleanup() {
base.TestCleanup();
}
}
Then my test:
[TestMethod]
public void Membership_CreateUser() {
_provider.UserRepository = new InMemoryUserRepository();
_provider.CreateUser(_email, out _status);
Assert.AreEqual(MembershipCreateStatus.Success, _status);
}
This answer provided inspiration: https://stackoverflow.com/a/13073558/1803682
Since you've exposed your repository as a property, just create an instance of your provider in your test class and set that property to your mock like so:
public void Test()
{
MyMembershipProvider provider = new MyMembershipProvder();
provider.UserRepository = mock.Object;
// Do test stuff here
// Verify mock conditions
}
Presumably your repository implementation is using the UserRepository property so when you test it, this code will use the mocked dependency.
You can setup a test module for Ninject and create the Ninject Kernel using the test module within the unit test project.
This is what an inversion of control container is for. You have one set of bindings configured for running as a website, another set for running in test, another set for running using a different backend (or whatever).
I do this so that the production and test code get initialized in the same fashion (via Ninject) and all that changes is the configuration of Ninject.
Or do what #chris house suggested. That will work too.
I'm using Entity Frameworc 4.1 Code First and Moq. And I want to test database initializer. Also I have the abstract BaseUnitOfWork class which inherited from DbContext (so, for testing it should be mocked).
public abstract class BaseUnitOfWork : DbContext, IUnitOfWork
{
...
public IDbSet<User> Users
{
get
{
return Set<User>();
}
}
...
}
User is simple POCO with three properties: Id, Login, Password.
And here is the code of the DbInitializer:
public class BaseDbInitializer : DropCreateDatabaseAlways<BaseUnitOfWork>
{
protected override void Seed(BaseUnitOfWork context)
{
base.Seed(context);
context.Set<User>().Add(new User { Login = "admin", Password = "1" });
context.SaveChanges();
}
}
I'm trying to test this initializer with the next test (NUnit is used):
[TestFixture]
public class BaseDbInitializerTests
{
private BaseUnitOfWork _baseUnitOfWork;
[TestFixtureSetUp]
public void Init()
{
Database.SetInitializer(new BaseDbInitializer());
_baseUnitOfWork = new Mock<BaseUnitOfWork>(Consts.ConnectionStringName).Object;
_baseUnitOfWork.Database.Initialize(true);
}
[TestFixtureTearDown]
public void CleanUp()
{
_baseUnitOfWork.Dispose();
Database.Delete(Consts.ConnectionStringName);
}
[Test]
public void ShouldInitializeBaseDb()
{
var repository = new Mock<BaseRepository<User>>(_baseUnitOfWork).Object;
var firstUserInDb = repository.FindBy(x => x.Login == "admin" && x.Password == "1").SingleOrDefault();
Assert.That(firstUserInDb, Is.Not.Null);
Assert.That(firstUserInDb.Login, Is.EqualTo("admin"));
Assert.That(firstUserInDb.Password, Is.EqualTo("1"));
}
}
Unfortunately it seems like Seed method of the BaseDbInitializer class doesn't execute. DB is recreating, but there is no any records, and I tried to debug this test and Seed method was executed during debug session.
The strategy DropCreateDatabaseAlways<BaseUnitOfWork> is looking for an exact type match of BaseUnitOfWork, not a derived type, and not Mock<BaseUnitOfWork>. If you need it, you'll have to implement a copy of the strategy for the mocked type.
What is the point of mocking context and in the same time expecting database to exists? The point of mocking is removing dependency on the database (but it will not always work as expected).
So either use mock (unit test with all its problems) or database with real context and integration test.