Существуют ли диагностические помощники для тестирования конфигурации MS.DI? - PullRequest
0 голосов
/ 02 октября 2019

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

ВВ прошлом мы использовали Castle.Windsor в качестве нашего поставщика услуг, который поставляется с диагностическими классами и функциями, чтобы помочь решить эти проблемы. Существуют ли аналогичные функции для MS.DI или нам нужно что-то делать самим?

1 Ответ

0 голосов
/ 11 октября 2019

Хотя я согласен с советом Стивена и Криса, разработчики не всегда отвечают за инфраструктуру, которую они должны использовать. Где возможно, я буду настаивать на Castle.Windsor, так как это то, с чем моя команда больше всего знакома, но в этом случае нам удалось собрать воедино те тесты, которые мы хотели сами.

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

[Test]
public void Assert_Lifetimes_Are_Consistent()
{
    var missing = new List<string>();
    var errors = new HashSet<Tuple<string, string>>();

    foreach (var service in _serviceCollection.Where(s => IsInYourAssembly(s.ServiceType)))
    {
        var serviceLifetimeRanking = LifetimeRanking(service.Lifetime);

        foreach (var fieldInfo in ((System.Reflection.TypeInfo)service.ServiceType).DeclaredFields.Where(fi => fi.FieldType.IsAbstract && IsInYourAssembly(fi.FieldType)))
        {
            var dependencyLifetime = _serviceCollection.SingleOrDefault(fi => fi.ServiceType == fieldInfo.FieldType)?.Lifetime;

            if (dependencyLifetime==null)
                missing.Add($"No service found for {fieldInfo.FieldType.FullName} as a dependency for {service.ServiceType.FullName}");

            var dependencyLifetimeRanking = LifetimeRanking(dependencyLifetime);

            if (dependencyLifetimeRanking > serviceLifetimeRanking)
                errors.Add(
                    Tuple.Create(
                        $"{service.ServiceType.Name} ({service.Lifetime})",
                        $"{fieldInfo.FieldType.Name} ({dependencyLifetime})"
                    )
                );
        }
    }

    if (missing.Any()||errors.Any())
    {
        var sb = new StringBuilder();

        sb.AppendJoin(Environment.NewLine, missing);

        if (errors.Any())
        {
            sb.AppendLine("Following dependency pairs have inconsistent lifestyles:");
            sb.AppendLine(string.Join(Environment.NewLine, errors.Select(err => $"{err.Item1} -> {err.Item2}")));
        }

        Assert.Fail(sb.ToString());
    }
}

private bool IsInYourAssembly(Type type)
{
    return (type.Assembly.FullName?.IndexOf("YOUR_PROJECT_ASSEMBLY_HERE") ?? -1) == 0;
}
private int LifetimeRanking(ServiceLifetime serviceLifetime)
{
    switch (serviceLifetime)
    {
        case ServiceLifetime.Singleton:
            return 1;
        case ServiceLifetime.Scoped:
            return 2;
        case ServiceLifetime.Transient:
            return 3;
        default:
            throw new ArgumentOutOfRangeException("serviceLifetime", serviceLifetime,
                $"Value is not a known member of the ServiceLifetime enum");
    }
}

Если тест не пройден,он вернет список отсутствующих зависимостей, за которым следует список несовместимых сроков зависимости.

Поле _serviceCollection должно быть заполнено и Startup(config, env).ConfigureServices(_serviceCollection); необходимо вызвать перед выполнением теста.

IsInYourAssembly - важная функция для фильтрации всех универсальных типов, которые также возвращаются в _serviceCollection.

...