В настоящее время я пытаюсь использовать Autofixture для создания предопределенного устройства в качестве реализации ICustomization для ApplicationDbContext с использованием Поставщик In-Memory .
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;
}
}
Затем я применю эту настройку к своему Fixture следующим образом:
[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
/// <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;
}
}
Теперь в моем модульном тесте утверждение Assert.Empty(dbContext.Users);
будет выбрасывать System.NotImplementedException : The method or operation is not implemented.
, поскольку DbSet<AppUser> Users
, сгенерированный из Autofixture, является DynamicProxy.
См. Изображение dbContext.Users как DynamicProxy
Как ни странно, если я проверяю точки останова из метода фабрики (ie. CreateDbContext()
) вызывается из fixture.Create<ApplicationDbContext>()
, пользователи DbSet относятся к ожидаемому типу.
См. Изображение dbContext.Users как InternalDbSet
По желанию, я знаю, что могу заменить все использование от dbContext.Users
до dbContext.Set<User>()
, и это позволит пройти модульный тест, но проблема в том, что в реальном классе я использую dbContext.Users
для IQueryables d операций с базой данных, поэтому мне все еще нужно придерживаться этого, если это возможно.
Следовательно, мне нужна помощь, чтобы узнать, почему AutoFixture использует мой метод фабрики для генерации экземпляра для моего ApplicationDbContext, но все свойства DbSet<>
внутри него высмеиваются при разрешении ISpecimenBuilder. Есть ли способ исправить это?
Я опубликовал аналогичный вопрос в их Github , но он не был активен в последнее время, поэтому я также спросил здесь.
Пожалуйста, поймите, я только начал использовать Автокрепление 2 дня go. Поэтому, если я что-то пишу неправильно или в каком-либо шаблоне проектирования есть неправильное представление, пожалуйста, напишите комментарий, чтобы я мог принять это за урок.
Обновление 1 : Итак, я попытался использовать инициализированное обычное устройство без какой-либо настройки AutoMoq (ie. fixture = new Fixture()
), и на этот раз оно выдает исключение AutoFixture.ObjectCreationExceptionWithPath
, жалуясь, что не может разрешить свойство DbSet в ApplicationDbContext. В этот момент я подумал, если кто-нибудь знает, как использовать Relay или ISpecimenBuilder, чтобы сказать Autofixture, что нужно использовать / вызывать / реализовывать все свойства DbSet<T>
в ApplicationDbContext с dbContext.Set<T>
, потому что это сработает, если я заменю все использование DbSets в мои модульные тесты, но, как я уже говорил, все IQueryable возвращаются из DbSets, поэтому я не могу просто заменить его в ApplicationDbContext.
Обновление 2 : я удаляю и упрощаю создание ApplicationDbContext из моего фабричный метод CreateDbContext (), так как это вызовет путаницу из-за сложности кода.