ArchUnit ile Yazılım Mimarini Test Et

Hakan ŞEREMET
Paycell Tech Team
Published in
3 min readDec 27, 2023

Bu yazımda yazılım süreçlerinin en önemli parçalarından biri olan yazılım mimarilerinin ArchUnit ile nasıl test edilebileceğinden bahsedeceğim.

Geliştirme ekosistemlerinde birçok prensip ve deseni öğrenmemiz ve bunları uygulamamız gerekiyor. Domain-Driven Design (DDD) ile projenin ana etkileşim alanlarını modellerken, Onion Architecture ile katmanların içerden dışarı doğru dizayn edebiliyoruz. Hexagonal Architecture uygulamak istersek Port lar ve Adaptor ler tanımlayarak sistemimizin daha esnek olmasını sağlayabiliyoruz. Solid prensipleri ve Object-Oriented Programming (OOP) ile kodumuzu açık, modüler ve yeniden kullanılabilir hale getiriyoruz. Ayrıca Command Query Responsibility Segregation (CQRS) ile sorgulama ve komut sorumluluklarını ayırarak performans ve ölçeklenebilirlik avantajları elde ediyoruz.

Peki Mimari Kararlarımızı Nasıl Denetliyoruz ?

Birçok yazılım uygulaması doğar doğmaz belli mimari kalıplara ve prensiplere son derece bağlı bir şekilde ilerler ancak ilerki zamanlarda bu kararlara uyum esnetilir esnetilir esnetilir …

Ve günün sonunda neredeyse hiçbir mimariye uymayan karmaşık akışlı düzensiz yapılar, bakım zorlukları ve istenmeyen bağımlılıklarla birlikte düzeltme zorluklarıyla karşılaşırız.

Tüm bu olumsuz durumlarla karşılaşmak istemeyen yazılım ekipleri daha sıkı Code Review lar yapar. Hatta Code Review Standartları, çizelgeler ve nice yöntem denerler. Bu çabaların elbetteki kodun kalitesine ve mimarinin korunmasına katkısı vardır. Ancak insan bağımlı bir sistem her zaman esnetilmeye ve hataya açıktır.

Peki Biz Ne yapmalıyız ?

Sayın W. Bennis in ideasına ulaşabilecek miyiz bilinmez ancak biz Code Review sürelerimizi azaltmak, insan kaynaklı hataları ortadan kaldırmak ve tabi mimarimizin test edilmesini, denetlenmesini bir otomasyona bağlayabilmek için bugün ArchUnit kullanımından bahsedeceğiz.

ArchUnit (https://www.archunit.org/) , Java projelerinde mimari kuralları tanımlamak, uygulamak ve test etmek için kullanılan bir Java kütüphanesidir. Bu kütüphane, yazılım projelerinde tanımlanan mimari kuralların korunup korunmadığını kontrol etmek için kullanılır.

Aşağıdaki bağımlılı projenize eklediğinizde ArchUnit’in test kapsamında kullanılmasını sağlar.

Maven Kullanıyorsanız :


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

Gradle Kullanıyorsanız :

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

Neleri Test Edebiliriz ?

  • Paketlerin seçilen mimariye uygun olup olmadığını kontrol etmek.
@AnalyzeClasses(packages = "com.example")
public class LayeredArchitectureTest {

private static final JavaClasses classes = new ClassFileImporter().importPackages("com.example");

@ArchTest
public static final ArchRule layeredArchitecture =
Architectures.layeredArchitecture()
.layer("Core").definedBy("com.example.core..")
.layer("Application").definedBy("com.example.application..")
.layer("Infrastructure").definedBy("com.example.infrastructure..")

.whereLayer("Application").mayOnlyBeAccessedByLayers("Core")
.whereLayer("Infrastructure").mayOnlyBeAccessedByLayers("Application", "Core")
.whereLayer("Core").mayNotBeAccessedByAnyLayer();
}
  • Sınıfların ve paketlerin belirli diğer sınıflar veya paketlere olan bağımlılıklarını kontrol etmek.
@AnalyzeClasses(packages = "com.example")
public class DependencyTest {

private static final JavaClasses classes = new ClassFileImporter().importPackages("com.example");

@ArchTest
public static final ArchRule noDependencyCycles =
SlicesRuleDefinition.slices()
.matching("com.example.(*)..")
.should().beFreeOfCycles();
}
  • Sınıfların, paketlerin veya metotların belirli adlandırma kurallarına uymasını kontrol etmek.
@AnalyzeClasses(packages = "com.example")
public class NamingConventionTest {

private static final JavaClasses classes = new ClassFileImporter().importPackages("com.example");

@ArchTest
public static final ArchRule classesShouldBeNamedCorrectly =
classes().should().haveSimpleNameEndingWith("Service")
.andShould().resideInAPackage("..service..");
}
  • Belirli anotasyonların belirli sınıflara veya paketlere uygulanıp uygulanmadığını kontrol etmek.
@AnalyzeClasses(packages = "com.example")
public class AnnotationTest {

private static final JavaClasses classes = new ClassFileImporter().importPackages("com.example");

@ArchTest
public static final ArchRule classesShouldBeAnnotatedCorrectly =
classes().should().beAnnotatedWith(Entity.class)
.andShould().resideInAPackage("..domain..");
}
  • Belirli sınıfların veya paketlerin belirli arayüzleri uygulayıp uygulamadığını kontrol etmek.
  • Javadoc belgelerin varlığını ve içeriğini kontrol etmek.
  • Java modüllerinin belirli modülleşme kurallarına uyup uymadığını kontrol etmek.
  • Test sınıflarının belirli paketlere veya sınıflara bağlı olup olmadığını kontrol etmek.

Daha birçok örnek verilebilir.

Bu adresten de diğer örneklere bakabilirsiniz;

https://github.com/TNG/ArchUnit-Examples/tree/main

Reference :

--

--