Настройка автофиксатора для встроенной памяти DbContext - PullRequest
1 голос
/ 27 февраля 2020

В настоящее время я пытаюсь использовать 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 (), так как это вызовет путаницу из-за сложности кода.

1 Ответ

0 голосов
/ 04 марта 2020

Трудно понять, чего вы пытаетесь достичь из своего поста.

Я думаю, что вам действительно нужно, это протестировать ваш код, который использует EntityFramework. Если это так, возможно, вы захотите взглянуть на эту библиотеку, я создал EntityFrameworkCore.AutoFixture . Он использует провайдера базы данных In-Memory, а также провайдера SQLite in-memory.

Посмотрите readme для некоторых примеров кода. Если у вас есть какие-либо вопросы, напишите мне сообщение или откройте вопрос на GitHub.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...