Saat membuat perangkat lunak, kita semua, sebagai tim, setuju untuk mengikuti serangkaian pedoman yang umumnya dianggap praktik terbaik. Namun, selama pengembangan, developer mungkin secara tidak sengaja atau tidak sengaja melanggar aturan ini. Kami biasanya mengandalkan tinjauan kode atau pemeriksa kualitas kode seperti SonarQube , PMD, dll. untuk memeriksa pelanggaran tersebut. Tetapi beberapa rekomendasi mungkin merupakan solusi yang tidak dapat diotomatiskan dengan SonarQube, PMD, dll.
Misalnya, saya biasanya ingin mengikuti pedoman di bawah ini untuk aplikasi berbasis Java saya:
Ikuti struktur tiga tingkat (web, layanan, tingkat repositori) di mana setiap tingkat hanya dapat berinteraksi dengan tingkat bawah langsung, dan tingkat yang lebih rendah tidak boleh berinteraksi dengan tingkat atas. itu. tingkat web dapat berinteraksi dengan tingkat layanan, tingkat layanan dapat berinteraksi dengan tingkat repositori. Tetapi tingkat repositori tidak dapat berkomunikasi dengan layanan atau tingkat web, tingkat layanan tidak dapat berinteraksi dengan tingkat web.
Jika aplikasinya besar, kita mungkin ingin mengikuti struktur Package-By-Feature, di mana hanya komponen Web dan Service yang bersifat publik dan komponen lainnya harus bersifat package-private.
Saat menggunakan Injeksi Ketergantungan Musim Semi, jangan gunakan injeksi berbasis lapangan dan lebih suka injeksi berbasis konstruktor.
Jadi, mungkin ada banyak aturan yang ingin kita ikuti. Kabar baiknya adalah kami dapat memverifikasi implementasi rekomendasi ini menggunakan pengujian JUnit menggunakan ArchUnit .

Berikut panduan User ArchUnit .
Mari kita lihat bagaimana kita dapat menggunakan ArchUnit untuk menguji pedoman arsitektur kita.
Tambahkan dependensi archunit-junit5 berikut .
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit-junit5</artifactId>
<version>0.13.1</version>
<scope>test</scope>
</dependency>Mari kita lihat bagaimana kita dapat menerapkan berbagai pedoman yang saya sebutkan di atas.
Aturan 1. Layanan dan repositori tidak boleh berinteraksi dengan tingkat web.
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 .
.
@Test
void 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
@Test
void 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 ..
@Test
void 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 :
@Test
void 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);
}, .
Silakan baca ArchUnit UserGuide resmi dan cari tahu hal - hal hebat apa yang dapat Anda lakukan dengan ArchUnit .