وحدة تختبر هندسة مشروع Spring Boot باستخدام ArchUnit

عند إنشاء البرامج ، تحدد فرق التطوير عادةً مجموعة من الإرشادات والاتفاقيات للترميز والتي تعتبر أفضل الممارسات.

هذه هي الأساليب التي عادة ما يتم توثيقها وإبلاغ فريق التطوير الذي اعتمدها بالكامل. ومع ذلك ، أثناء التطوير ، يمكن للمطورين كسر هذه الإرشادات ، والتي تم العثور عليها أثناء مراجعات الكود أو من خلال مدقق جودة الكود.

لذلك ، فإن أحد الجوانب المهمة هو أتمتة هذه التوجيهات قدر الإمكان في جميع أنحاء بنية المشروع لتحسين عمليات التحقق.

يمكننا تنفيذ هذه الإرشادات كاختبارات JUnit يمكن التحقق منها باستخدام  ArchUnit . يضمن ذلك إيقاف إنشاء إصدار البرنامج في حالة حدوث انتهاك للبنية.

ArchUnit  هي مكتبة  مجانية وبسيطة وقابلة للتوسيع لاختبار بنية كود Java الخاص بك لاستخدامها في أي بيئة اختبار بسيطة لوحدة Java. أي أن ArchUnit يمكنها التحقق من التبعيات بين الحزم والفئات والمستويات والشرائح والتحقق من التبعيات الدائرية وغير ذلك الكثير. يقوم بذلك عن طريق تحليل كود Java معطى واستيراد جميع الفئات في بنية كود Java.  

ArchUnit , :

. ArchUnit.

ArchUnit JUnit 5, Maven Central: 

pom.xml

XML

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

build.gradle

Groovy

dependencies { 
  testImplementation 'com.tngtech.archunit:archunit-junit5:0.14.1' 
} } 

Java

class ArchunitApplicationTests {

  private JavaClasses importedClasses;

  @BeforeEach
  public void setup() {
        importedClasses = new ClassFileImporter()
                .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
                .importPackages("com.springboot.testing.archunit");
    }

  @Test
  void servicesAndRepositoriesShouldNotDependOnWebLayer() {

      noClasses()
                .that().resideInAnyPackage("com.springboot.testing.archunit.service..")
                .or().resideInAnyPackage("com.springboot.testing.archunit.repository..")
                .should()
                .dependOnClassesThat()
                .resideInAnyPackage("com.springboot.testing.archunit.controller..")
                .because("Services and repositories should not depend on web layer")
                .check(importedClasses);
    }
}

-.

class ArchunitApplicationTests {

  private JavaClasses importedClasses;

  @BeforeEach
    public void setup() {
        importedClasses = new ClassFileImporter()
                .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
                .importPackages("com.springboot.testing.archunit");
    }
    @Test
    void serviceClassesShouldOnlyBeAccessedByController() {
        classes()
                .that().resideInAPackage("..service..")
                .should().onlyBeAccessed().byAnyPackage("..service..", "..controller..")
                .check(importedClasses);
    }
}

ArchUnit API-, DSL, , , .  .

( AspectJ Pointcuts). 

Java

class ArchunitApplicationTests {
  
  private JavaClasses importedClasses;

  @BeforeEach
  public void setup() {
    importedClasses = new ClassFileImporter()
        importedClasses = new ClassFileImporter()
                .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
                .importPackages("com.springboot.testing.archunit");
  }

    @Test
    void serviceClassesShouldBeNamedXServiceOrXComponentOrXServiceImpl() {
        classes()
                .that().resideInAPackage("..service..")
                .should().haveSimpleNameEndingWith("Service")
                .orShould().haveSimpleNameEndingWith("ServiceImpl")
                .orShould().haveSimpleNameEndingWith("Component")
                .check(importedClasses);
    }

    @Test
    void repositoryClassesShouldBeNamedXRepository() {
        classes()
                .that().resideInAPackage("..repository..")
                .should().haveSimpleNameEndingWith("Repository")
                .check(importedClasses);
    }
    @Test
    void controllerClassesShouldBeNamedXController() {
        classes()
                .that().resideInAPackage("..controller..")
                .should().haveSimpleNameEndingWith("Controller")
                .check(importedClasses);
    }
}

— . , Service, Component . .

Java

class ArchunitApplicationTests {
  private JavaClasses importedClasses;

  @BeforeEach
  public void setup() {
      importedClasses = new ClassFileImporter()
              .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
              .importPackages("com.springboot.testing.archunit");
  }

  @Test
  void fieldInjectionNotUseAutowiredAnnotation() {

      noFields()
              .should().beAnnotatedWith(Autowired.class)
              .check(importedClasses);
  }
  @Test
  void repositoryClassesShouldHaveSpringRepositoryAnnotation() {
      classes()
              .that().resideInAPackage("..repository..")
              .should().beAnnotatedWith(Repository.class)
              .check(importedClasses);
  }
  @Test
  void serviceClassesShouldHaveSpringServiceAnnotation() {
      classes()
              .that().resideInAPackage("..service..")
              .should().beAnnotatedWith(Service.class)
              .check(importedClasses);
  }
}

API ArchUnit Lang Java.  , , .

class ArchunitApplicationTests {

	private JavaClasses importedClasses;

	@BeforeEach
  public void setup() {
        importedClasses = new ClassFileImporter()
                .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
                .importPackages("com.springboot.testing.archunit");
    }
    @Test
    void layeredArchitectureShouldBeRespected() {

					layeredArchitecture()
                .layer("Controller").definedBy("..controller..")
                .layer("Service").definedBy("..service..")
                .layer("Repository").definedBy("..repository..")
                .whereLayer("Controller").mayNotBeAccessedByAnyLayer()
                .whereLayer("Service").mayOnlyBeAccessedByLayers("Controller")
                .whereLayer("Repository").mayOnlyBeAccessedByLayers("Service")
                .check(importedClasses);
    }
}

Spring Boot , .

تقدم ArchUnit مجموعة من الوظائف للتحقق مما إذا كانت بنية الطبقات الخاصة بك ملتزمة. توفر هذه الاختبارات تأكيدات تلقائية بأن الوصول والاستخدام يتم الحفاظ عليهما ضمن الحدود التي تحددها. لذلك ، يمكنك كتابة القواعد الخاصة بك. في هذه المقالة ، وصفنا عدة قواعد. يقدم التوثيق الرسمي لـ ArchUnit العديد من الاحتمالات.

يمكن العثور على الكود المصدري الكامل للأمثلة في مستودع GitHub الخاص بي  .




All Articles