Внедрение зависимостей в проекте Xunit

Я работаю над веб-приложением ASP.Net Core MVC.

Мое решение содержит 2 проекта:

  • Один для приложения и
  • Второй проект, посвященный модульным тестам (XUnit).

Я добавил ссылку на проект приложения в проект Tests.

Сейчас я хочу написать класс в проекте XUnit Tests, который будет связываться с базой данных через структуру сущностей.

Что я делал в своем проекте приложения, так это доступ к моему классу DbContext через внедрение зависимостей конструктора.

Но я не могу этого сделать в моем тестовом проекте, потому что у меня нет файла Startup.cs. В этом файле я могу объявить, какие услуги будут доступны.

Итак, что я могу сделать, чтобы получить ссылку на экземпляр моего DbContext в тестовом классе?

Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Лучшие практики использования Guice в ваших Java-проектах
Лучшие практики использования Guice в ваших Java-проектах
Guice от Google - это популярная система инъекции зависимостей для Java-приложений. Он помогает разработчикам создавать более ремонтопригодный,...
25
0
23 854
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Вы можете реализовать своего собственного поставщика услуг для решения проблемы DbContext.

public class DbFixture
{
    public DbFixture()
    {
        var serviceCollection = new ServiceCollection();
        serviceCollection
            .AddDbContext<SomeContext>(options => options.UseSqlServer("connection string"),
                ServiceLifetime.Transient);

         ServiceProvider = serviceCollection.BuildServiceProvider();
    }

    public ServiceProvider ServiceProvider { get; private set; }
}

public class UnitTest1:IClassFixture<DbFixture>
{
    private ServiceProvider _serviceProvider;

    public UnitTest1(DbFixture fixture)
    {
        _serviceProvider = fixture.ServiceProvider;
    }

    [Fact]
    public void Test1()
    {
        using (var context = _serviceProvider.GetService<SomeContext>())
        {
        }
    }
}

Но имейте в виду, что использование EF внутри модульного теста - не лучшая идея, и лучше имитировать DbContext.

Анатомия хорошего модульного тестирования

Что такое DatabaseSeeder? Следует ли мне поместить DbFixture в подпапку службы?

Bob5421 19.06.2018 08:44

Ошибка копирования и вставки. Вы можете поместить DbFixture в любом месте тестового проекта. Я создаю папку TestSetup или TestInfrastructure и помещаю в эти папки такие классы.

Mohsen Esmailpour 19.06.2018 09:08

Вместо вашей строки с AddEntityFrameworkSqlServer я использую это: serviceCollection.AddDbContext <BDDContext> (options => options.UseSqlServer ("connection string") Есть большая разница?

Bob5421 19.06.2018 09:15

Нет, оба работают нормально и AddEntityFrameworkSqlServer регистрирует дополнительные сервисы.

Mohsen Esmailpour 19.06.2018 09:20

Еще один момент: вы уверены, что мне не нужно где-то «объявлять» этот класс DbFixture? У меня есть предупреждение, в котором говорится, что класс никогда не создается, и у меня есть ошибка времени выполнения: Следующие параметры конструктора не имеют совпадающих данных фикстуры: DbFixture fixture

Bob5421 19.06.2018 09:23

привет @MohsenEsmailpour, можете ли вы ответить на этот вопрос здесь, мне нужно захватить все службы и использовать их по вызову для интеграции дальше, а не только контекст базы данных, спасибо! stackoverflow.com/questions/57331395/…

user11860043 02.08.2019 22:45
stackoverflow.com/questions/57331395/… это еще один отличный вопрос
user11721944 03.08.2019 02:08

@AlanWalker Конечно, я отвечу на вопрос как можно скорее.

Mohsen Esmailpour 03.08.2019 12:16

Пытался создать образец, но не запустился (LinqPad 6: Query -> Add XUnit Test Support, а затем вставил свой код; обычно это хорошая отправная точка для тестирования). Как ссылаться на ServiceCollection, ServiceLifetime, ServiceProvider? Не могли бы вы дать короткий отрывок для SomeContext?

Matt 24.11.2020 11:46

Анатомия хорошего модульного тестирования - к сожалению, предоставленная вами ссылка не работает, не могли бы вы найти ее и исправить, пожалуйста?

Matt 24.11.2020 11:47
Важный:.UseSqlServer требует загрузки пакета Microsoft.EntityFrameworkCore.SqlServer. Это связано с сообщила о проблеме № 7891 с EF Core..
Matt 25.11.2020 15:14

Хорошо, чтобы проверить это, я использовал Adventureworks, добавил класс SomeContext с public SomeContext(DbContextOptions options) : base(options) и public DbSet<AWBuildVersion> AWBuildVersions { get; set; }, реализовал AWBuildVersion со всеми свойствами и добавил запрос context.AWBuildVersions.Select(s=>s.DatabaseVersion).FirstOr‌​Default().Dump(); в оператор using во фрагменте кода. Этот запрос выдает ошибку. Тип объекта AWBuildVersion требует определения первичного ключа. «Но я использовал атрибут [Key] для SystemInformationID. Вы знаете, как заставить его работать?

Matt 26.11.2020 10:44

После использования аннотаций [Key], [Table ("TableName")] и [Column ("ColumName"] он все еще не работал. Затем я выяснил, почему: я определил свойства в классе таблицы "как есть", т.е. без { get; set; } - после добавления что работает нормально!

Matt 26.11.2020 10:59

@Matt Я исправил неработающую ссылку.

Mohsen Esmailpour 30.11.2020 12:26

@MohsenEsmailpour - Спасибо, это действительно хорошее резюме о том, как правильно разрабатывать (писать) модульные тесты.

Matt 30.11.2020 16:50

Для модульных тестов вам нужно издеваться над своим контекстом.

Есть отличный пакет nuget для насмешек, который называется Moq.

Некоторая помощь для начала работы:

public ClassName : IDisposable
{
    private SomeClassRepository _repository;
    private Mock<DbSet<SomeClass>> _mockSomeClass;

    public ClassName() 
    {
        _mockSomeClass = new Mock<DbSet<SomeClass>>();

        var mockContext = new Mock<IApplicationDbContext>();
        mockContext.SetupGet(c => c.SomeClass).Returns(_mockSomeClass.Object);

        _repository = new SomeClassRepository(mockContext.Object);
    }

    public void Dispose()
    {
        // Anything you need to dispose
    }

    [Fact]
    public void SomeClassTest()
    {
        var someClass = new SomeClass() { // Initilize object };

        _mockSomeClass.SetSource(new[] { someClass });

        var result = _repository.GetSomethingFromRepo( ... );

        // Assert the result
    }
}

Для интеграционных тестов вы делаете то же самое, но настройки следующие:

_context = new ApplicationDbContext();

Убедитесь, что ваш TestClass унаследован от IDisposable (TestClass : IDisposable), чтобы вы могли удалить контекст после каждого теста.

https://xunit.github.io/docs/shared-context

В xunit нет атрибута SetUp, atrribute. Вместо этого используйте конструктор.

Mirek Michalak 30.01.2019 16:16

Вы можете использовать Xunit.DependencyInjection

Здесь было бы очень полезно рассказать о том, что это делает и почему он лучше других. Также было бы здорово добавить небольшой образец, демонстрирующий, как он выглядит при использовании.

noelicus 09.05.2019 17:32
stackoverflow.com/questions/63269460/… Работаем над проблемой
Golden Lion 05.08.2020 18:52

Вы можете использовать пакет Microsoft.EntityFrameworkCore.InMemory

var _dbContextOptions = new DbContextOptionsBuilder<DbContext>().UseInMemoryDatabase(Guid.NewGuid().ToString()).Options;

А потом

var context = new DbContext(_dbContextOptions);

Другие вопросы по теме