Проверяем Правильность Архитектуры ПО с Помощью Тестов
Архитектура ПО — это план того, как структурировать систему. Вы можете строго или не очень строго следовать ему. Но когда сроки поджимают, и вы начинаете срезать углы, созданная вами прекрасная программная архитектура рассыпается, как карточный домик.
Архитектурные тесты — это автоматизированные тесты, которые проверяют структуру и дизайн кода. Вы можете использовать их для проверки соблюдения архитектуры ПО или направления зависимостей ваших проектов.
Архитектурные тесты пишутся так же, как и любой модульный тест. Есть отличная библиотека для создания архитектурных тестов, в которой уже реализован шаблонный код - NetArchTest.Rules.
Отправной точкой для написания архитектурных тестов является статический класс Types, который можно использовать для загрузки набора типов. После этого их можно дополнительно отфильтровать. Некоторые из доступных методов фильтрации:
- ResideInNamespace
- AreClasses
- AreInterfaces
- HaveNameStartingWith
- HaveNameEndingWith
Наконец, вы можете написать правило, которое хотите применить, вызвав Should или ShouldNot и применив условие, которое хотите проверить. Вот пример проверки того, что все классы домена запечатаны:
var result = Types
.InAssembly(DomainAssembly)
.That()
.AreClasses()
.Should()
.BeSealed()
.GetResult();
Assert.True(result.IsSuccessful);
Архитектурные тесты особенно полезны для проверки соблюдения правил архитектуры ПО в многоуровневой архитектуре или модульном монолите.
- Домен не должен иметь никаких зависимостей:
var result = Types
.InAssembly(DomainAssembly)
.ShouldNot()
.HaveDependencyOnAny("Application", "Infrastructure")
.GetResult();
Assert.True(result.IsSuccessful);
- Приложение не должно зависеть от Инфраструктуры:
var result = Types
.InAssembly(AplicationAssembly)
.Should()
.NotHaveDependencyOn("Infrastructure")
.GetResult();
Assert.True(result.IsSuccessful);
- Инфраструктура должна зависеть от Приложения и Домена
Здесь правило немного сложнее, т.к. мы пишем не отрицательный, а положительный фильтр. Поэтому ограничим выборку более конкретным набором типов. Например, что все репозитории должны иметь зависимость от пространства имен Домена.
var result = Types
.InAssembly(InfrastructureAssembly)
.HaveNameEndingWith("Repository")
.Should()
.HaveDependencyOn("Domain")
.GetResult();
Assert.True(result.IsSuccessful);
Ещё один ценный вариант использования — проверка соблюдения правил проектирования. Правила проектирования более конкретны и сосредоточены на деталях реализации классов. Например:
- Сервисы должны быть internal-классами,
- Сущности и объекты-значения должны быть запечатаны,
- Контроллеры не могут напрямую зависеть от репозиториев:
var result = Types
.InAssembly(ApiAssembly)
.That()
.HaveNameEndingWith("Controller")
.ShouldNot()
.HaveDependencyOn("Infrastructure.Repositories")
.GetResult();
Assert.True(result.IsSuccessful);
Итого
Архитектурные тесты — это простой способ проверить правильность архитектуры ПО и выполнения правил проектирования с помощью автоматизированных тестов. Вы можете быстро написать их и кардинально снизить затраты на проверки соблюдения правил архитектуры ПО, которые в противном случае выполнялись бы вручную через парное программирование или обзоры кода.
Источник: https://www.milanjovanovic.tech/blog/enforcing-software-architecture-with-architecture-tests