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

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