Добавление инъекции зависимостей в ваши консольные приложения (и тесты) .NET

Добрый день, ребята!

Вы, вероятно, заметили, что внедрение зависимостей (Dependency Injection, DI) в наши дни стало нормой дефакто, и, возможно, это вполне оправданно, поскольку оно (при правильном использовании) приводит к созданию тестируемой, изменяемой и разделенной кодовой базы. Но, возможно, одна из проблем (по крайней мере, в экосистеме ASP.NET) заключается в том, что поскольку большая часть начальной подготовки выполняется при создании нового проекта ASP.NET, может быть непонятно и неясно, как настроить DI в автоматизированных тестах или других проектах верхнего уровня, которые не имеют готовых «лесов», как веб-проекты ASP.NET. Существует несколько руководств о том, как это сделать, но вот минималистичное руководство.

О, прежде чем продолжить, я упомяну, что в примерах, показанных здесь, будет использоваться контейнер DI от Microsoft, который поставляется в комплекте с проектами ASP.NET Core. Концепции, рассматриваемые здесь, будут аналогичными для других контейнеров, но сам код будет немного отличаться.

Во-первых, давайте упорядочим некоторые пакеты

Когда дело доходит до версионности, есть шанс, что они уже установлены в других проектах вашего решения. Поэтому, пока вы не смешиваете и не сопоставляете версии .NET между проектами, имеет смысл оставить версии одинаковыми. По этой причине я просто перечислю пакеты, которые вам нужны, и предоставлю вам самим определить подходящую версию.

  1. Microsoft.Extensions.Configuration
  2. Microsoft.Extensions.Configuration.Json
  3. Microsoft.Extensions.DependencyInjection
  4. Microsoft.Extensions.DependencyInjection.Abstractions
  5. Microsoft.Extensions.Options

Добавьте настройки вашего приложения

Когда дело доходит до конфигурации, у вас, вероятно, есть некоторые пользовательские настройки приложения, определенные в файле appsettings.*.json в существующем веб-проекте ASP.NET. Вам нужно будет перенести его в новый (голый) проект, чтобы применить те же настройки. У вас есть несколько вариантов, как это сделать:

1) Создать пользовательский файл настроек для вашего нового проекта; 2) Скопировать существующий файл appsettings.json из вашего веб-проекта в новый; или 3) Добавить ссылку из существующего файла appsettings.json в ваш новый проект.

Я обычно предпочитаю вариант 3, так как любые изменения, внесенные в файл appsettting.json в вашем основном веб-проекте, вступят в силу и в новом проекте, и вам не придется поддерживать два (или более) конфигурационных файла.

* Внешний вид этого файла может немного отличаться в Rider или VS for Mac, но он все равно должен быть там.

Настройте все это

Здесь я предлагаю создать класс xxxBootstrapper для обработки загрузки DI-контейнера и любой другой настройки, где xxx — это префикс вашего проекта. Например: Если это тестовый проект, вы можете назвать его TestBootstrapper.

Например:

    public static class TestBootstrapper
    {
        public static void Bootstrap(IServiceCollection services)
        {
            // todo: Add any test services
        }
    }

Войти в полноэкранный режим Выйти из полноэкранного режима

Это замечательно, но как мне на самом деле создать экземпляр IServiceCollection?

Спросите вежливо 🙂 Если серьезно, просто создайте новую ServiceCollection, которая является конкретной реализацией IServiceCollection.

    public static class ServiceProviderFactory
    {
        public static IServiceProvider CreateServiceProvider()
        {
            var services = new ServiceCollection();

            LibraryBootstrapper.Bootstrap(services);
            TestBootstrapper.Bootstrap(services);

            return services.BuildServiceProvider();
        }
    }

Вход в полноэкранный режим Выход из полноэкранного режима

Поскольку мы, вероятно, будем делать это в каждом отдельном тесте, а логика бутстрапинга будет расти по мере развития нашего решения, я поместил это в фабрику для удобства.

Собираем все вместе

Вот оно 🙂 Обратите внимание, что Setup и Test являются специфичными для NUnit атрибутами, которые могут отличаться от того, что вам нужно будет использовать в зависимости от используемого вами фреймворка тестирования — хотя вы должны использовать NUnit, потому что все остальные глупы.

*** Вышеприведенное утверждение — это шутка. Используйте любой фреймворк для тестирования, который вам нравится.

using ConsoleDependencyInjection.Library;
using Microsoft.Extensions.DependencyInjection;

namespace ConsoleDependencyInjection.Tests;

public class MyServiceTests
{
    private IMyService _sut;

    [SetUp]
    public void Setup()
    {
        _sut = ServiceProviderFactory
            .CreateServiceProvider()
            .GetService<IMyService>();
    }

    [Test]
    public void Test1()
    {
        Assert.DoesNotThrow(() =>
        {
            _sut.DoTheThing();
        });
    }
}

Вход в полноэкранный режим Выход из полноэкранного режима

В методе Setup (который запускается перед каждым тестом) мы создаем экземпляр поставщика услуг из фабрики, которую мы создали ранее, и, используя подход Service Locator к DI, запрашиваем у поставщика услугу, которую хотим протестировать. Чтобы увидеть все это в действии, вы можете посмотреть репозиторий на Github.

Спасибо за чтение! До встречи!

Оцените статью
devanswers.ru
Добавить комментарий