Autofixture: Setting up an fixture for In-memory DbContext
See original GitHub issueHi, I have make the same post in SO. Feel free to comment there.
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()
{
DbContextOptions<ApplicationDbContext> dbContextOptions = null;
var builder = new DbContextOptionsBuilder<ApplicationDbContext>();
var configurationOptions = new ApplicationStoreOptions
{
ConfigureDbContext = dbCtxBuilder =>
{
dbContextOptions =
(DbContextOptions<ApplicationDbContext>)
dbCtxBuilder.UseInMemoryDatabase(DatabaseName).Options;
}
};
configurationOptions.ConfigureDbContext(builder);
var dbContext = new ApplicationDbContext(dbContextOptions, configurationOptions);
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<AppUser> 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<T> 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.
Issue Analytics
- State:
- Created 4 years ago
- Comments:5 (3 by maintainers)
Top GitHub Comments
Thank you for the feedback @takato1314.
I can see your point. The scenario you’re describing is not something that I encountered which is why it was not implemented in the library so far.
I have created a new issue for the scenario you described, so you can track the feature implementation. Feel free to add comments to the issue and submit new issues, if you encounter any.
Since the issue is being tracked in aivascu/EntityFrameworkCore.AutoFixture#24, I’m going to close this one. Feel free to reopen, if you’d like to discuss this further.