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();
Related
Problem:
I am new to writing unit testing in c# using xunit. So I am trying to mock the MongoDB connection. In my project I have use repository pattern there I have used unit of work class like this. so I am accessing every repository via it. so the unit of work class code is here.
namespace QuestionBank.API.Repositories
{
public class UnitOfWork : IUnitOfWork
{
public readonly IQuestionsBankDBContext _context;
private readonly ILogger<UnitOfWork> _logger;
private Dictionary<Type, object> repositories;
private IQuestionsRepository _questionsRepository;
private ICampaignQuestionsRepository _campaignQuestionsRepository;
private ICandidateAnswerRepository _candidateAnswerRepository;
private IIntergrationEventLogRepository _integrationEventLogRepository;
private IControlRepository _controlRepository;
public UnitOfWork(IQuestionsBankDBContext context, ILogger<UnitOfWork> logger)
{
_context = context;
_logger = logger;
}
public IQuestionsRepository QuestionsRepository
{
get
{
this._questionsRepository = new QuestionsRepository(_context as IQuestionsBankDBContext, this, _logger);
return this._questionsRepository;
}
}
public ICandidateAnswerRepository CandidateAnswerRepository
{
get
{
this._candidateAnswerRepository = new CandidateAnswerRepository(_context as IQuestionsBankDBContext, this, _logger);
return this._candidateAnswerRepository;
}
}
public ICampaignQuestionsRepository CampaignQuestionsRepository
{
get
{
this._campaignQuestionsRepository = new CampaignQuestionsRepository(_context as IQuestionsBankDBContext, this, _logger);
return this._campaignQuestionsRepository;
}
}
public IIntergrationEventLogRepository IntegrationEventLogRepository
{
get
{
this._integrationEventLogRepository = new IntergrationEventLogRepository(_context as IQuestionsBankDBContext, this, _logger);
return this._integrationEventLogRepository;
}
}
public IControlRepository ControlRepository
{
get
{
this._controlRepository = new ControlRepository(_context as IQuestionsBankDBContext, this, _logger);
return this._controlRepository;
}
}
public IGenericRepository<TDocument> GetRepository<TDocument>() where TDocument : IDocument
{
if (this.repositories == null)
{
this.repositories = new Dictionary<Type, object>();
}
var type = typeof(TDocument);
if (!this.repositories.ContainsKey(type))
{
this.repositories[type] = new GenericRepository<TDocument>(_context);
}
return (IGenericRepository<TDocument>)this.repositories[type];
}
}
}
So in the unit test to mock services and repositories, I need to pass database context to unitofwork. I tried it this way.
var mockDbContext = new Mock<QuestionsBankDBContext>();
var dbContext = mockDbContext.Object;
var mock = new Mock<ILogger<UnitOfWork>>();
_logger = mock.Object;
unitOfWork = new UnitOfWork(dbContext, _logger);
questionsService = new QuestionsService(unitOfWork);
campaignQuestionsService = new CampaignQuestionsService(unitOfWork);
tokenService = new TokenService();
stringLocalizer = new Mock<IStringLocalizer<SharedResource>>();
questionBankIntergrationEventService = new Mock<IQuestionBankIntergrationEventService>();
questionsController = new QuestionsController(questionsService, campaignQuestionsService, stringLocalizer.Object, tokenService, questionBankIntergrationEventService.Object);
contextMock = new Mock<HttpContext>();
And this is my DB context class.
using MongoDB.Driver;
using QuestionBank.API.Models;
namespace QuestionBank.API.Data
{
public class QuestionsBankDBContext : IQuestionsBankDBContext
{
public IMongoClient Client { get; set; }
public IMongoDatabase Database { get; set; }
public QuestionsBankDBContext(IQuestionBankDatabaseSettings settings)
{
Client = new MongoClient(settings.ConnectionString);
Database = Client.GetDatabase(settings.DatabaseName);
}
}
}
Then I wrote a unit test like this.
[Theory]
[InlineData("61879e54e86be1fa5e41831f")]
[InlineData("61879e54e86be1fa5e41831e")]
public async Task GetQuestionById(string questionId)
{
var actionResult = await questionsController.GetQuestionById(questionId);
var result = actionResult as ObjectResult;
Assert.NotNull(result.Value);
if (result.StatusCode == (int)System.Net.HttpStatusCode.OK)
{
Assert.IsType<Questions>(result.Value);
}
else if (result.StatusCode == (int)System.Net.HttpStatusCode.NotFound)
{
Assert.Contains("ErrorCode", result.Value.ToString());
}
else if (result.StatusCode == (int)System.Net.HttpStatusCode.InternalServerError)
{
var code = (int)ErroCodes.InternalServerError;
Assert.Contains(code.ToString(), result.Value.ToString());
}
}
Then when running this it gives
And my question controller GetQuestionById is like this.
[HttpGet]
//[Authorize(Roles = "SuperAdmin,Admin")]
[Route("getquestionbyidfrombank")]
[ProducesResponseType(typeof(Questions), 200)]
[ProducesResponseType(typeof(string), 404)]
[ProducesResponseType(typeof(string), 500)]
public async Task<IActionResult> GetQuestionById([FromQuery] string questionId)
{
try
{
string errorText;
if (!string.IsNullOrEmpty(questionId))
{
var question = await
questionsService.GetQuestionById(questionId);
return Ok(question);
}
else
{
errorText = string.Format(stringLocalizer[Constants.ErrorCodeString],
(int)ErroCodes.SpecifiedItemNotFound,
stringLocalizer[Helper.ToEnumString(ErroCodes.SpecifiedItemNotFound)]);
return StatusCode(404, errorText);
}
}
catch (Exception ex)
{
string exceptionData =
$"Exception occured while getiing question by id. " +
$"\nException Data: Message- {ex.Message}; " +
$"InnerException- {ex.InnerException}; StackTrace- {ex.StackTrace}";
string errorText = string.Format(stringLocalizer[Constants.ErrorCodeString],
(int)ErroCodes.InternalServerError,
stringLocalizer[Helper.ToEnumString(ErroCodes.InternalServerError)]);
return StatusCode(500, errorText);
}
}
this is how I do the service instantiation.
public QuestionsService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
Get question by id function
public async Task<Questions> GetQuestionById(string id)
{
var question = await _unitOfWork.QuestionsRepository.FindByIdAsync(id);
return question;
}
Can someone help me to write this unit test correctly and solve this issue. I tried a lot to find out a way to do this but I could not able to do it. Thank you
Don't - just because something can be mocked, doesn't mean it should.
Instead, you can a docker image to run Mongo, drop and create a DB per test class init, drop collections per test init.
Testing a DAL (data-access-layer) without the DB is a waste of time and won't help you actually find bugs.
When you unit test your other components, mock the entire DAL to return the objects you expect.
And do not skip writing a test for your entire service that includes an empty/pre-populated-via-test-config-data DB.
Also, the fields in your DbContext should be private, not public and while passing a constant instance of a DbContext to MongoDB is OK, because MongoDB context is stateless (no transactions and connections are pooled), in general passing a DbContext via constructor is wrong (because relational DBs have transactions and the connections should not be kept open), instead pass a Func<DbContext> (e.g. public MyClass(Func<DbContext> contextConstructor)) which returns the constructor and have DbContext implement IDisposable. This way the client class can do using (context = contextCreator()) { ... }.
You should create a mock of the interface IQuestionsBankDBContext not the class QuestionsBankDBContext. Apart from that your test is not a unit test, it's more of an integration test because you are creating an instance of a controller and some services, and you mock only the database layer of your application. In unit tests you should test only a single layer. If you write unit tests for a controller then you should mock the services and their behavior (the direct dependencies of the controller).
Looking at your controller method, first unit test should check that when you pass not null and not empty question id then your method returns OkResult. For this test the mock of questionsService could return a question object.
Next test should check the opposite case, when it gets empty string it should return a response with code 404. (In my opinion it should be 400 but we are talking about unit tests)
Last test should check the exception handling. For example you pass a valid questionId in the test, but the mock of questionService throws an exception. In this test you can assert that the response has status code 500.
If you want to test many cases with one test method (a Theory in XUnit) then all these cases should have the same result, or you should provide an expected result with each case.
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.
I would like to create a Details view with entity framework data using a repository pattern.
This is my interface repository:
public interface InterfaceRepositroy: IDisposable
{
IEnumerable<SubjectContent> GetAll();
SubjectContent Get(string id);
}
This is the toher repository:
public class SubjectRepository : InterfaceRepositroy,IDisposable
{
private irfwebpage20161013070934_dbEntities2 db;
public IEnumerable<SubjectContent> GetAll()
{
return db.Set<SubjectContent>().ToList();
}
public SubjectContent Get(string id)
{
return db.Set<SubjectContent>().Find(id);
}
public void Dispose()
{
throw new NotImplementedException();
}
}
Here is my controller:
private InterfaceRepositroy subjectreposi;
public ActionResult Details(string id)
{
SubjectContent subject = subjectreposi.Get(id);
return View(subject);
}
My View is a standard details template.
It gets an error at this point in the controller:
SubjectContent subject = subjectreposi.Get(id);
I would really appreciate the help. This is like the fourth version of a repository pattern i am trying to implement but none of them worked so far. I have tried it without interface, with the instance of the subjecrepository or with different linq to sql in the repository. It either gets http error or it doesnt show the data just the names of the data.
Create constructors that initialise your data context:
public SubjectRepository()
{
db = new irfwebpage20161013070934_dbEntities2();
}
public SubjectRepository(irfwebpage20161013070934_dbEntities2 dbContext)
{
db = dbContext;
}
This allows you to either initialise your repository with no parameters which will initialise you data context or specify your own data context.
You can now use this like this:
var repo = new SubjectRepository();
SubjectContent subject = repo.Get(id);
I have a project created using Asp.Net Core, but I have a problem with unit testing one part of my controller's action, I use xUnit.net(2.2.0-beta2-build3300) for testing and Moq(4.6.25-alpha) for mocking, and FluentAssertions(4.13.0) and GenFu(1.1.1) to help me with my tests, I have a Unit of Work class (note that I cut it down to what's relevant to my question):
public class UnitOfWork : IUnitOfWork
{
private readonly WebForDbContext _context;
public UnitOfWork(WebForDbContext context)
{
_context = context;
}
private IContactRepository _contactRepository;
public IContactRepository ContactRepository
{
get
{
if (this._contactRepository == null)
{
this._contactRepository = new ContactRepository(_context);
}
return _contactRepository;
}
}
}
In my ContactRepository I have:
public class ContactRepository:IContactRepository
{
private WebForDbContext _context;
public ContactRepository(WebForDbContext context)
{
_context = context;
}
public Task<int> AddNewContactAsync(Contact contact)
{
_context.Contacts.Add(contact);
return _context.SaveChangesAsync();
}
}
I inject the Unit of Work to my controller, my action:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(ContactViewModel contactViewModel)
{
var contactWioutJavascript = _webForMapper.ContactViewModelToContact(contactViewModel);
int addContactResultWioutJavascript = await _uw.ContactRepository.AddNewContactAsync(contactWioutJavascript);
if (addContactResultWioutJavascript > 0)
{
return View("Success");
}
}
What I want to do is to stub my AddNewContactAsync method to return an integer bigger than 0 (10 in this case), to inter the if clause, and test to see if the correct view is returned, my test class:
public class ContactControllerTests
{
private Mock<IUnitOfWork> _uw;
private Mock<IWebForMapper> _webForMapper;
public ContactControllerTests()
{
_uw = new Mock<IUnitOfWork>();
_webForMapper = new Mock<IWebForMapper>();
}
[Fact]
public async Task Create_SouldReturnSuccessView_IfNewContactAdded()
{
var contactViewModel = A.New<ContactViewModel>();
_webForMapper.Setup(s => s.ContactViewModelToContact(contactViewModel)).Returns(A.New<Contact>());
_uw.Setup(u => u.ContactRepository.AddNewContactAsync(A.New<Contact>())).ReturnsAsync(10);
var sut = new ContactController(_uw.Object, _webForMapper.Object);
var result = (ViewResult)await sut.Create(contactViewModel);
result.ViewName.Should().Be("Success");
}
}
But the method AddNewContactAsync returns 0, and my test doesn't enter the if condition that leads to return View("Success"), I know that the problem doesn't have to do with ReturnAsync because I've used it with other async methods and it works, also _webForMapper is stubbed correctly and map the view model to my domain model and contactWioutJavascript is populated with value, but when I debug the test and reach the addContactResultWioutJavascript line, it returns 0, no matter what I do.
The things I did, but didn't work:
I mocked the ContactRepository, and tried to stub that instead:
_contactRepository.Setup(c => c.AddNewContactAsync(A.New<Contact>())).ReturnsAsync(10);
_uw.SetupGet<IContactRepository>(u => u.ContactRepository).Returns(_contactRepository.Object);
I also found other questions:
Moq Unit of Work
how to moq simple add function that uses Unit of Work and Repository Pattern
Mocking UnitOfWork with Moq and EF 4.1
But none of them helped, I'd appreciate any help.
You are almost there. Two things:
You do need to setup the ContactRepository property as Moq doesn't support "chaining" of setups.
Also, you need to use It.IsAny<>() instead of A.New<>():
_contactRepository.Setup(c => c.AddNewContactAsync(It.IsAny<Contact>())).ReturnsAsync(10);
This says "match any Contact that is passed in". When you used A.New<>(), you were saying "match the Contact instance that I just created with A.New<>(). In effect, that will never match anything since you didn't save or use the return value of A.New<>().
I have a View that is bound to a NewUserViewModel which is posted to this method of the controller.
[HttpPost]
public ActionResult NewUser(NewUserViewModel newUser)
{
var user = new User();
user.Id = newUser.Id;
user.Email = newUser.Email;
//more mapping hidden for brevity
//here is where the trouble starts
_userService.AddNewUser(user);
return RedirectToAction("Users");
}
The _userService is in a private field that is instantiated in the controllers constructor like this
private IUserService _userService;
public ControllerName()
{
_userService = new UserService();
}
The AddNewUser method on the _userService looks like this.
public void AddNewUser(User newUser)
{
using (var uow = new UnitOfWorkUser(new Context()))
{
using (var _userRepo = new UserRepository(uow))
{
_userRepo.InsertOrUpdate(newUser);
uow.Save();
}
}
}
The constructor of the UserRepository looks like this.
private Context _context;
public UserRepository(UnitOfWorkUser unitOfWork)
{
_context = unitOfWork.Context;
}
and the unitOfWorkLooks like this.
public class UnitOfWorkUser :IDisposable, IUnitOfWork
{
private readonly Context _context;
public UnitOfWorkUser(Context context = null)
{
_context = context ?? new Context();
}
public int Save()
{
return _context.SaveChanges();
}
internal Context Context
{
get { return _context; }
}
public void Dispose()
{
_context.Dispose();
}
}
And the InsertOrUpdate Method on the _userRepo looks like this.
public virtual void InsertOrUpdate(User user)
{
if (user.Id == default(int))
{
_context.Users.Add(user);
}
else
{
_context.Entry(user).State = System.Data.EntityState.Modified;
}
}
When I get to the
_context.Users.Add(user);
in the method above I get this error
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
I thought by passing in the Context with the UnitOfWork Object in the constructor of the UserRepository I was going to be avoiding these errors.
What am I doing wrong?
There is a better approach to use UOW in asp.net mvc, you dont consider many aspects of entity life time in context, so I suggest reading this article
This looks very wrong to me. The purpose of unit work pattern is to consolidate all your "work" in one object. There is several issue with the following code:
Looks like you are disposing the DBContext Twice
Since you only need dbcontext, you shouldn't only pass dbcontext to the repository instead of UOW object
You might want to have a internal reference to the UserRepository. It should be used to group your Repositories and ensure they all share the same EF context instance. A sample will look like UnitOfWorkUser.UserRepo.Save(newUser)
using (var uow = new UnitOfWorkUser(new Context()))
{
using (var _userRepo = new UserRepository(uow))
{
_userRepo.InsertOrUpdate(newUser);
uow.Save();
}
}
Here is an example on how you use UOW,
http://www.mattdurrant.com/ef-code-first-with-the-repository-and-unit-of-work-patterns/