Setting up an Autofixture for In-memory DbContext - c#

I am currently trying to use Autofixture to create a pre-defined fixture as an implementation of ICustomization for ApplicationDbContext using In-Memory provider.
public class ApplicationDbContextFixture : ICustomization
{
public void Customize(IFixture fixture)
{
var specimenFactory = new SpecimenFactory<ApplicationDbContext>(CreateDbContext);
fixture.Customize<ApplicationDbContext>(
composer =>
composer.FromFactory(specimenFactory)
);
}
/// <summary>
/// Private factory method to create a new instance of <see cref="ApplicationDbContext"/>
/// </summary>
private ApplicationDbContext CreateDbContext()
{
var dbContextOptions = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase("SomeDatabaseName")
.Options;
var dbContext = new ApplicationDbContext(dbContextOptions);
return dbContext;
}
}
Then, I will apply that customization to my Fixture as follows:
[Fact]
public void TestAddUsersToEmptyDatabase()
{
// Arrange
// Fixture for ApplicationDbContext
var fixture = FixtureFactory.CreateFixture();
var applicationDatabaseFixture = new ApplicationDbContextFixture();
fixture.Customize(applicationDatabaseFixture);
// Fixture for users
var randomUser = fixture.Create<AppUser>();
var normalUser = fixture.Create<AppUser>();
var adminUser = fixture.Create<AppUser>();
// Act & Assert
// Run the test against one instance of the context
// Use a clean instance of the context for each operation too
using (var dbContext = fixture.Create<ApplicationDbContext>())
{
Assert.Empty(dbContext.Users);
dbContext.Users.Add(randomUser);
dbContext.SaveChanges();
}
using (var dbContext = fixture.Create<ApplicationDbContext>())
{
dbContext.Users.AddRange(normalUser, adminUser);
dbContext.SaveChanges();
}
using (var dbContext = fixture.Create<ApplicationDbContext>())
{
Assert.NotEmpty(dbContext.Users);
Assert.NotNull(dbContext.Users.SingleOrDefault(_ => _.Id == randomUser.Id));
Assert.NotNull(dbContext.Users.SingleOrDefault(_ => _.Id == normalUser.Id));
Assert.NotNull(dbContext.Users.SingleOrDefault(_ => _.Id == adminUser.Id));
}
}
FixtureFactory.CreateFixture implementation
/// <summary>
/// Factory method to declare a single <see cref="IFixture"/> for unit tests applications
/// </summary>
internal static class FixtureFactory
{
internal static IFixture CreateFixture()
{
var fixture = new Fixture().Customize(
new AutoMoqCustomization { ConfigureMembers = true });
return fixture;
}
}
Now in my unit test, asserting the Assert.Empty(dbContext.Users); will throw System.NotImplementedException : The method or operation is not implemented. because the DbSet<AppUser> Users generated from Autofixture is a DynamicProxy.
See image dbContext.Users as DynamicProxy
Oddly enough if I inspect the breakpoints from the factory method (ie. CreateDbContext()) called from the fixture.Create<ApplicationDbContext>(), the DbSet Users is of the expected type.
See image dbContext.Users as InternalDbSet
Optionally, I do aware that I can replace all the usage of dbContext.Users to dbContext.Set<User>() and that would make the unit test pass but the problem is that in the actual class, I am using the dbContext.Users for IQueryables and database operations, so I still need to stick with it if possible.
Hence, I would need help to know why does AutoFixture used my factory method to generate the instance for my ApplicationDbContext but all the DbSet<> properties inside it are mocked when resolved by the ISpecimenBuilder. Is there a way to remedy this?
I've post the similar question in their Github but it has been not active recently, so i also asked here.
Kindly please understand I only started to use Autofixture 2 days ago. So if there's something that I write wrong or there's a misconception in any Design Patterns, please kindly wrote a comment so that I can take it as a lesson.
Update 1:
So i tried to use initialized a plain fixture without any AutoMoq customization (ie. fixture = new Fixture()) and this time it throws a AutoFixture.ObjectCreationExceptionWithPath exception, complaining that it is unable to resolve DbSet property within the ApplicationDbContext. At this point, I was thinking if anyone know how to use a Relay or ISpecimenBuilder to tell Autofixture to use/call/implement all DbSet<T> properties within the ApplicationDbContext with dbContext.Set<T> because that would work if I replace all usage of DbSets in my unit tests, but as I mentioned, all IQueryable are return from DbSets so i cannot simply just replace it in ApplicationDbContext.
Update 2:
I remove and simplify the creation of ApplicationDbContext from my factory method CreateDbContext() since it will cause confusion from the code complexity.

It is hard to understand what you are trying to achieve from your post.
I think what you actually need, is to test your code that happens to use EntityFramework.
If that's the case you might want to have a look at this library, I have created EntityFrameworkCore.AutoFixture. It uses the In-Memory database provider as well as SQLite in-memory provider.
Have a look at the readme for some code examples. If you have any questions drop me a message or open an issue on GitHub.

Related

How to test a controller that uses IMemoryCache?

I have a controller with the definition:
public async Task<ResponseDto> MethodNameController(List<string> identifiers)
{
if(!_cache.TryGetValue(key, out IDictonary<string, string> result))
{
result = await service.GetMethodService(activityContextObject, identifiers)
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromSeconds(80))
.SetAbsoluteExpiration(TimeSpan.FromSeconds(120))
.SetSize(1024);
_cache.Set(key, result, cacheEntryOptions);
}
return new ResponseDto{ //ResponseDto Object split into many fields};
}
The constructor is as below
public Controller(serviceName, httpContextAccessor, IOptionsSnapshot_config, IMemoryCache)
{
_serviceName = serviceName,
_httpContextAccessor = httpContextAccessor,
_config = IOptionsSnapshot_config
_cache = IMemoryCache
}
I'm facing difficulty in unit testing this method. I followed this blog, this SO answer and many more trynig to replicate them in my scenario and failed. I tried passing regular memory cache object(because I'm unable to mock it) but invoking controller returns error slidingExpirationTime should be positive, current time set is 00:00:00 I'm unable to set the sliding expiration in test classes.
Can anyone guide me to correct resources or tell me how I can test this method. I'm new to writing xUnit tests, so any help will be great(For learning moq etc).
The easiest would be to mock it using some libraries, like AutoFixture, Moq.
Generally you mock interfaces as follows:
var fixture = new Fixture() //creates objects that has fine logic in creating objects
.Customize(new AutoMoqCusotmization); // customize the fixture to automoq interfaces
var cacheMock = fixture.Create<Mock<IMemoryCache>>();
// use cacheMock.Setup method
another, simplier option would be to use directly Mock:
var cacheMock = new Mock<IMemoryCache>();
// use cacheMock.Setup method
If you can not use any of the above you can define dummy implmentation for the service, like:
internal class DummyMemoryCache : IMemoryCache
{
// implement interface
)
and use it in place of IMemoryCache.

EF Core DbContext Concurrency Issue with XUnit Tests

I seem to have a strange concurrency issue I can't seem to put my finger on.
When I construct my implementation of a DbContext I inject the Entities I want to be built by the modelbuilder (don't worry about why). This is only done once by my app at runtime and runs fine, but when I'm integration testing DB integration, I inject only the test entities I need for my InMemoryDatabase.
Now I seem to have a weird issue where two unit tests in different class files needing different entities seem to get crossed over.
I keep running the unit tests and the first test will pass, but the second test will fail saying that TestObjectB doesn't exist in the model. When I inspect the model it says TestObjectA exists instead, even though it wasn't injected on this test. So as if the implementation of the DataContext was static and overwritten...These are different files and fresh constructors for the context, I don't understand how they are crossing paths? If I run the unit test that fails on it's own, it passes.
Note the following code has been simplified for your view.
DB Context:
public class DataContext : DbContext
{
private readonly List<IEntity> _entities;
public DataContextA(List<IEntity> entities, DbContextOptions options) : base(options)
{
_entities = entities;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var entity in _entities)
{
modelBuilder.Entity(entity.GetType());
}
}
}
Test Implementation 1:
[Fact]
public void CheckUniqueFieldA()
{
var options = new DbContextOptionsBuilder<DataContext>();
options.UseInMemoryDatabase(Guid.NewGuid().ToString());
using (var context = new DataContext(new List<IEntity> { new TestObjectA() }, options.Options))
{
//Do Something
}
}
Test Implementation2:
[Fact]
public void CheckUniqueFieldB()
{
var options = new DbContextOptionsBuilder<DataContext>();
options.UseInMemoryDatabase(Guid.NewGuid().ToString());
using (var context = new DataContext(new List<IEntity> { new TestObjectB() }, options.Options))
{
//Do Something
}
}
The reason is EF Core model caching described in Alternating between multiple models with the same DbContext type documentation topic:
...the model caching mechanism EF uses to improve the performance by only invoking OnModelCreating once and caching the model.
By default EF assumes that for any given context type the model will be the same.
The link also contains an example how to solve it. You'd need to create custom implementation of IModelCacheKeyFactory interface and replace the default EF Core implementation using ReplaceService inside OnConfiguring. The implementation should return an object representing the unique cache key for a given DbContext instance. The default implementation simply returns context.GetType().

Testing CRUD class.

I have some issue. Im writing some unit test in my project but i don't know how to test my CRUD methods.. Maybe they are not testable ;/
This is one of my methods:
public static void IncrementInvalidLoginColumn(string login)
{
User user;
using (DTContext context = new DTContext())
{
try
{
user = context.Users.Where(u => u.Login.CompareTo(login) == 0).FirstOrDefault();
if (user.InvalidLogins < 3)
{
user.InvalidLogins = user.InvalidLogins + 1;
}
context.SaveChanges();
}
catch
{
}
}
}
Maybe someone will have idea what should i do.
It depends on what you mean by "unit" test. If you don't want your test to hit the database then your method is not testable (or at least not without some refactoring).
If hitting the database is acceptable (which would actually be an integration test) then you can definitely test your method.
Here are some steps:
1. Arrange the initial data. You use an instance of the DTContext directly in the test to put the system in a predefined state (basically you write some user records in the database)
You run the method you want to test (which in fact uses its own instance of the DTContext)
You use DTContext again to read the user information directly from the database and assert that the InvalidLogins property has incremented.
You need to make sure you delete any data that you put in manually.
This is the gist of DI:
public class Example {
private IDatabaseGateway myDatabase;
public Example(IDatabaseGateway myDb) {
myDatabase = myDb;
}
public void DoStuff() {
...
myDatabase.GetData();
...
}
}
You give your business class an abstraction of the database via the constructor, that is you inject your dependencies in the class that needs them.
Once you have this in place, in production code you pass in the constructor a concrete instance of IDatabaseGateway that goes to the actual database.
In the case of a unit test you pass it a mock instance of the same interface. The mock is a special object that you can setup/configure to return what you want. Various libraries exist for mocking (an easy one is Moq).
However without modifying your code too much, it is better to stick with integration testing that hits the database. It will give you a simple and valid test.
Especially since there are some pitfalls in mocking the DbContext in EF (ex. some queries may not work when you will use them in production, testing updates in EF with mocks is a bit trickier).
Ok so i read all of your posts and they was very helpful.
I use MOQ framework and this is example how i do it.
This is how Liviu M. told me to do for example:
public class CRUDclass
{
private DTContext _context;
public CRUDclass(DTContext myObj)
{
_context = myObj;
}
}
We have CRUD Class which are doing operations directly on our database. We have constructor with one argument and private field. This our context :)
This is (for example) my method in CRUDclass:
public bool AddUser(User user)
{
try
{
_context.Users.Add(user);
_context.SaveChanges();
return true;
}
catch
{
return false;
}
}
Ovecourse he have our DTContext class witch DBSet becouse i using entity framework. And after that i am able to write some test method:
[TestMethod]
public void Should_Add_User()
{
var mockSet = new Mock<DbSet<User>>();
var mockContext = new Mock<DTContext>();
mockContext.Setup(m => m.Users).Returns(mockSet.Object);
var usrCRUD = new UserCRUD(mockContext.Object);
var usr = new User();
usr.Login = "Login_Name";
usr.Email = "loginName#test.com";
usr.Password = "***";
usr.InvalidLogins = 0;
usr.RememberID = 0;
usrCRUD.AddUser(usr);
mockSet.Verify(m => m.Add(It.Is<User>(arg => arg.Login == "Login_Name")));
mockContext.Verify(m => m.SaveChanges(), Times.Once());
}
At first a have to set my fake object (Mock>).
This test method checks if our user was added to Mock :)
I hope it can help somebody, if anything will be unclear please write a question :)
The idea of unit tests is to test your ifs, switches, etc., not the database operations.
In your case you need an interface that is an abstration of DTContext. In the simplest case it might look as the following.
public interface IObjectContext : IDisposable
{
IEnumerable<User> Users { get; }
}
In more complicated cases you may need to use IQueryable<T> or IObjectSet<T> instead of IEnumerable<T>.
Add a partial class declaration of DTContext and make it implement IObjectContext. Add a constructor to the class that contains the method IncrementInvalidLoginColumn with a parameter of type IObjectContext. Now you can inject any instance of IObjectContext instead of creating it in your class. This instance can be a DTContext or a mock for testing. Your class is ready to be tested without connection to a real database.
NB. In case of instances of IDisposable it's better to inject a Func<IObjectContext> instead of IObjectContext. Then you can create an instance for each operation and dispose it immediately after.
If there are CRUD operations in your code then I will recommend to use MOQ framework for unit testing. Below links are quite helpful:
Quick Start
Code Project
Ideally you would inject your DTContext rather than creating a new one every time that the method is called. That way you could mock that object in your unit test and verify that it is called as expected.
Your constructor would look something like:
private readonly IDTContext _context;
public CrudClass(IDTContext context)
{
_context = context
}
With your method now looking like
public static void IncrementInvalidLoginColumn(string login)
{
User user;
try
{
user = _context.Users.Where(u => u.Login.CompareTo(login) == 0).FirstOrDefault();
if (user.InvalidLogins < 3)
{
user.InvalidLogins = user.InvalidLogins + 1;
}
_context.SaveChanges();
}
catch
{
// Handle errors
}
}
And then in your test, if you were using a framework like Moq, you would basically script how that object would behave and test against that. For instance, setting up the mocked IDTContext to always return the same user for your Users collection and SaveChanges() method will write the number of invalid logins to a variable that you could then test against.

Unit Testing and Mocking using RhinoMocks

I am trying to setup tests for my new projects and come across some difficulties.
I am using NUnit and Rhino Mocks.
The Code that I am trying to test is this,
public DocumentDto SaveDocument(DocumentDto documentDto)
{
Document document = null;
using (_documentRepository.DbContext.BeginTransaction())
{
try
{
if (documentDto.IsDirty)
{
if (documentDto.Id == 0)
{
document = CreateNewDocument(documentDto);
}
else if (documentDto.Id > 0)
{
document = ChangeExistingDocument(documentDto);
}
document = _documentRepository.SaveOrUpdate(document);
_documentRepository.DbContext.CommitChanges();
}
}
catch
{
_documentRepository.DbContext.RollbackTransaction();
throw;
}
}
return MapperFactory.GetDocumentDto(document);
}
And my testing code is as follows
[Test]
public void SaveDocumentsWithNewDocumentWillReturnTheSame()
{
//Arrange
IDocumentService documentService = new DocumentService(_ducumentMockRepository,
_identityOfSealMockRepository, _customsOfficeOfTransitMockRepository,
_accountMockRepository, _documentGuaranteeMockRepository,
_guaranteeMockRepository, _goodsPositionMockRepository);
var documentDto = new NctsDepartureNoDto();
documentDto.IsDirty = true;
documentDto.Id = 0;
//Act
var retDocumentDto = documentService.SaveDocument(documentDto);
//Assert
Assert.AreEqual(documentDto, documentDto);
}
private static IDbContext CreateMockDbContext()
{
var dbContext = MockRepository.GenerateMock<IDbContext>();
// setup expectations for DbContext mock
//dbContextMock.Expect(...)
// bind mock of the DbContext to property of repository.DbContext
_ducumentMockRepository.Expect(mock => mock.DbContext).Return(dbContext).Repeat.Any();
return dbContext;
}
I need to pass in a documentDto with say isDirty set and test if it returns the same object.
So I was thinking to use a Stub instead of a mock.
I need to to find out how to set expectations so I can test the logic on the code.
you need to mock or stub all of the components which you do not want to test. You should, as a rule of thumb only have a maximum of single mock object the rest should be stubs. Mock the things you want to verify interaction with and stub the things which you just want to provide data for your test.
you don't tell us what type your _documentRepository is so its hard to tell exactly what you are testing here, but to test this method the only thing you can do, IMHO, is check that if the IsDirty flag is set is check that the correct methods on the _documentRepository and the Context are called.
To do this I would create a mock _documentRepository and mock DbContext and set expectations that _documentRepository.SaveOrUpdate(document) is called with the document passed in. Actually looking again at the code you need to convert between the dto and the document. Currently this is being done in a method. I would create a interface and a class for this and make that interface a dependency of the class you are testing so that you can create a stub which returns a known document from the documentDto. This class could handle creating a new document or returning an existing one based on the id in the Dto. otherwise you'll have to know what type of document is returned.
something like:
var documentDto = new NctsDepartureNoDto();
documentDto.IsDirty = true;
documentDto.Id = 0;
IDbContext context = MockRepository.GenerateMock<IDbRepository>();
context.Expect(x=>x.BeginTransaction()).Return(MockRepository.GenerateStub<ITransaction>());
context.Expect(x=>x.CommitChanges());
then create a mock for the repository
IDocumentRepository repo = MockRepository.GenerateMock<IDocumentRepository>();
repo.Expect(x=>x.DbContext).Return(context).Repeat().Any();
repo.Expect(x=>x.SaveOrUpdate(Arg<Document>.Is.Any())).Return(MockRepository.GenerateStub<Document>);
This tests that you interact with the repository object correctly when the dirty flag is set. It shouldn't test that the document is saved correctly or that the correct document is returned when SaveOrUpdate is called, as this should be tested in the tests for the repository, not here.
'But wait!' I hear you cry, 'you said at the beginning that there should only be a single mock, and here we have 2!'. That's true, and I think that this shows a fault in your current design.
You should not, I don't think, be exposing the DBContext from your documentRepository. You seem to be doing so in order to use the transactions.
If your repository needs to be aware of the transactions then have methods on the repository that allow the transactions to be controlled (or hide the fact that the transactions exist inside the repository object completely). These methods might just delegate to the internal DbContext but it would then mean that the only mock would need to be the document repository object itself, and not the DbContext

unit testing a unit of work

new to unit testing. I have a unit of work that I am trying to unit test. I am probably missing something simple here. I am trying to unit test the Commit method. I am using nunit and moq.
public class UnitOfWork : IUnitOfWork
{
private readonly DbContext _context;
public UnitOfWork(DbContext ctx)
{
_context = ctx;
}
public void Commit()
{
_context.SaveChanges();
}
}
What do I need to do to test this?
You would insert a mock of the DBContext and then verify that the SaveChanges method is called on commit.
[Test]
public void Will_call_save_changes() {
var mockContext = new Mock<DBContext>();
var unitOfWork = new UnitOfWork(mockContext.Object);
unitOfWork.Commit();
mockContext.Verify(x => x.SaveChanges());
}
You'll need to mock the DbContext, and then verify that SaveChanges was called. Something like Moq can help you here.
That's one way of doing it.
An alternative I've come across is:
Create your edmx file, remove the custom tool so it doesn't autogenerate the entities.
Open the edmx file, right click and add code generation item - go to online templates under database and select the EF POCO mockobject generator. This creates two T4 templates (one for entities and another for the object context and mock object context).
The one T4 template will generate your poco entities for you. The other T4 template will create an interface you can extend to be used as a unit of work which is implemented in an actual object context and a mock object context. Extending it just requires you modify the T4 template to include an additional method on the generated interface (void SaveChanges()) and the implementation of that method on the mock object context.
I've found it to work very well.
Albeit for unit testing purposes, you wouldn't want to test your unit of work (unless verifying certain objects are added/deleted etc.). You would instead test repositories with predefined responsibilities - usually defined within context (ex. patient appointments).
You'd do something like this:
public class PatientAppointmentRepository : IPatientAppointmentRepository
{
//Injected via IOC in constructor
private readonly IUnitOfWork _unitOfWork;
private readonly IPatientAppointmentLogic _patientAppointmentLogic;
public void CreateAppointment(PatientAppointmentModel model)
{
var appointment = ModelMapper.Instance.To<PatientAppointment>(model);
var appointmentAdded = _patientAppointmentLogic.Add(appointment);
if(appointmentAdded)
_unitOfWork.SaveChanges();
}
}
public class PatientAppointmentLogic : IPatientAppointmentLogic
{
private readonly IUnitOfWork _unitOfWork; //Set via constructor
private readonly PatientLogic _patientLogic;
public bool Validate(PatientAppointment appointment)
{
if(appointment == null)
throw new ArgumentNullException("appointment");
//perform some logic here
return true;
}
public void Add(PatientAppointment appointment)
{
if(appointment == null)
throw new ArgumentNullException("appointment");
if(!Validate(appointment)) return; //Or throw an exception, up to you
var patient = _patientLogic.GetById(appointment.PatientId);
if(patient == null) return;
patient.PatientAppointments.Add(appointment);
}
}
It's really up to your to structure it appropiately. You could have another AppointmentLogic repository that has a base validation as an example.
Ideally, generic validation should not depend on external resources (such as a database).
You should be able to create a validation context in one swoop that would be used in further validation (first valid 'cheaply' before you validate 'expensively').
Sometimes all the 'values' you need for the validation is inside an entity you would need anyway, then use that as the validation context.
Best of luck!

Categories