Русский
Русский
English
Статистика
Реклама

Перевод Внедрение рекомендаций по структуре кода с использованием ArchUnit

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

Например, обычно я хочу следовать приведенным ниже рекомендациям для моих приложений на основе Java:

  1. Следуйте трехуровневой структуре(уровни веб, сервис, репозиторий), где любой уровень может взаимодействовать только с непосредственным нижним уровнем, а нижний уровень не должен взаимодействовать с верхним уровнем.т.е. веб-уровень может взаимодействовать с уровнем сервиса, уровень сервиса может взаимодействовать с уровнем репозитория. Но уровень репозитория не может взаимодействовать с сервисным или веб-уровнем, сервисный уровень не может взаимодействовать с веб-уровнем.

  2. Если приложение большое, мы могли бы захотеть следовать структуре Package-By-Feature, где только компоненты Web и Service являются public, а остальные компоненты должны быть package-private.

  3. При использовании внедрения зависимостей Spring не используйте внедрение на основе поля и предпочитайте внедрение на основе конструктора.

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

Вот здесь руководство пользователя ArchUnit.

Давайте посмотрим, как мы можем использовать ArchUnit для тестирования наших рекомендаций по архитектуре.

Добавьте следующую зависимость archunit-junit5.

<dependency>    <groupId>com.tngtech.archunit</groupId>    <artifactId>archunit-junit5</artifactId>    <version>0.13.1</version>    <scope>test</scope></dependency>

Давайте рассмотрим, как мы можем применять различные рекомендации, о которых я упоминал выше.

Правило 1. Сервисы и репозитории не должны взаимодействовать с веб-уровнем.

package com.sivalabs.moviebuffs;import com.tngtech.archunit.core.domain.JavaClasses;import com.tngtech.archunit.core.importer.ClassFileImporter;import com.tngtech.archunit.core.importer.ImportOption;import org.junit.jupiter.api.Test;import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.*;import static com.tngtech.archunit.library.Architectures.layeredArchitecture;class ArchTest {    @Test    void servicesAndRepositoriesShouldNotDependOnWebLayer() {      JavaClasses importedClasses = new ClassFileImporter()          .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)          .importPackages("com.sivalabs.moviebuffs");      noClasses()          .that().resideInAnyPackage("com.sivalabs.moviebuffs.core.service..")            .or().resideInAnyPackage("com.sivalabs.moviebuffs.core.repository..")          .should()            .dependOnClassesThat()            .resideInAnyPackage("com.sivalabs.moviebuffs.web..")          .because("Services and repositories should not depend on web layer")          .check(importedClasses);    }}

ArchUnit предоставляет DSL, который довольно очевиден, чтобы понять цель теста.Приведенный выше тестовый код читается как английское предложение, что позволяет легко понять, что мы тестируем.

Правило 2: следует придерживаться многоуровневой архитектуры

В типичном приложении SpringBootуровеньсервиса зависит от уровня репозитория, а уровни вебиконфигурации зависят от уровня сервиса. Мы не хотим, чтобы уровень Web или Config напрямую взаимодействовал с уровнем репозитория.

Мы можем проверить выполение этого ограничения с помощью следующего теста.

@Testvoid shouldFollowLayeredArchitecture() {  JavaClasses importedClasses = new ClassFileImporter()          .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)          .importPackages("com.sivalabs.moviebuffs");  layeredArchitecture()      .layer("Web").definedBy("..web..")      .layer("Config").definedBy("..config..")      .layer("Service").definedBy("..service..")      .layer("Persistence").definedBy("..repository..")      .whereLayer("Web").mayNotBeAccessedByAnyLayer()      .whereLayer("Service").mayOnlyBeAccessedByLayers("Config", "Web")      .whereLayer("Persistence").mayOnlyBeAccessedByLayers("Service")      .check(importedClasses);}

Правило 3: Spring @Autowired НЕ следует использовать с внедрением зависимости на основе поля

@Testvoid shouldNotUseFieldInjection() {    JavaClasses importedClasses = new ClassFileImporter()          .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)          .importPackages("com.sivalabs.moviebuffs");    noFields()      .should().beAnnotatedWith(Autowired.class)      .check(importedClasses);}

Правило 4: следует соблюдать соглашение об именах

Мы хотим следовать некоторым соглашениям об именах, например, все имена классов сервиса должны оканчиваться наServiceи т.д.

@Testvoid shouldFollowNamingConvention() {    JavaClasses importedClasses = new ClassFileImporter()        .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)        .importPackages("com.sivalabs.moviebuffs");    classes()        .that().resideInAPackage("com.sivalabs.moviebuffs.core.repository")        .should().haveSimpleNameEndingWith("Repository")        .check(importedClasses);    classes()        .that().resideInAPackage("com.sivalabs.moviebuffs.core.service")        .should().haveSimpleNameEndingWith("Service")        .check(importedClasses);}

Правило 5: следует использовать только JUnit 5

Мы хотим использоватьJUnit 5 вкачестве нашей среды тестирования.Но зависимость JUnit 4 могла попасть в путь к классам как транзитивная зависимость (хмTestcontainers хм), и мы могли случайно импортировать классы/аннотацииJUnit4,такие как@Test,Assert и т.д. по ошибке.

Мы можем ограничить использование классов JUnit 4 следующим образом:

@Testvoid shouldNotUseJunit4Classes() {    JavaClasses classes = new ClassFileImporter()        .importPackages("com.sivalabs.moviebuffs");    noClasses()        .should().accessClassesThat().resideInAnyPackage("org.junit")        .because("Tests should use Junit5 instead of Junit4")        .check(classes);    noMethods().should().beAnnotatedWith("org.junit.Test")        .orShould().beAnnotatedWith("org.junit.Ignore")        .because("Tests should use Junit5 instead of Junit4")        .check(classes);}

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

Пожалуйста, прочтите официальныйArchUnit UserGuideи узнайте о том, какие крутые вещи вы можете делать сArchUnit.

Источник: habr.com
К списку статей
Опубликовано: 12.11.2020 00:07:35
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Java

Лучшие практики

Тестирование

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru