12 Ekim 2022 Çarşamba

ArchUnit Kullanımı

Giriş
ArchUnit unit test gibi yazılarak projedeki tüm sınıfları dolaşıyor ve belli kurallara uyup uymadıklarını test ediyor.

Maven
Eğer JUnit anotasyonlarını (@ArchTest) gibi kullanmak istersek bu bağımlılıkları eklemek gerekir. Eğer bu anotasyonlara gerek yoksa sadece Core bağımlılık yeterli

Örnek
Şöyle yaparız
// if we are using JUnit 4
<dependency>
  <groupId>com.tngtech.archunit</groupId>
  <artifactId>archunit-junit4</artifactId>
  <version>0.14.1</version>
  <scope>test</scope>
</dependency>

// if we are using JUnit 5:
<dependency>
  <groupId>com.tngtech.archunit</groupId>
  <artifactId>archunit-junit5</artifactId>
  <version>0.14.1</version>
  <scope>test</scope>
</dependency>
Örnek - Core
Şöyle yaparız
<dependency>
  <groupId>com.tngtech.archunit</groupId>
  <artifactId>archunit</artifactId>
  <version>0.23.1</version>
  <scope>test</scope>
</dependency>
Gradle
Şöyle yaparız
testImplementation 'com.tngtech.archunit:archunit:0.16.0'
Kullanım
Açıklaması şöyle
An arch unit test is made up of a few common parts:

- Target the classes you want to analyze. This can be done with something like new ClassFileImporter().importPackagesOf() if you are working outside of JUnit. Or using the annotation AnalyzeClasses such as @AnalyzeClasses(packages="com.example.application") if you are working in JUnit. This gathering of classes can and should only happen once per test class as it can be an expensive, time-consuming process.

- Then the actual tests can be performed using the ArchUnit DSL.
Yani tüm kullanım şöyle
JavaClasses classes = ...
ArchRule rule = ...
rule.check(classes);

ArchRule Sınıfı
Bu sınıfı yaratmak için
1. Hazır kurallar kullanılabilir
2. Slice API kullanılabilir

Hazır Kurallar
Örnek
Şöyle yaparız
@AnalyzeClasses(packages = "com.tngtech.archunit.example.layers")
public class CodingRulesTest {

  @ArchTest
  private final ArchRule no_generic_exceptions = 
    NO_CLASSES_SHOULD_THROW_GENERIC_EXCEPTIONS;
}
classes metodu
Örnek - Paketteki Sınıflar ve Anotasyon
Şöyle yaparız Burada belirli bir paketteki sınıfların @Entity anotasyonuna sahip olduğu kontrol ediliyor
@ArchTest
static final ArchRule entities_must_reside_in_a_model_package =
  classes().that().areAnnotatedWith(Entity.class)
    .should().resideInAPackage("..model..")
    .as("Entities should reside in a package '..model..'")
    .allowEmptyShould(true);
Benzer bir kural şöyle. Burada belirli bir paketteki sınıfların @Configuration anotasyonuna sahip olduğu kontrol ediliyor
@ArchTest
static final ArchRule configs_must_reside_in_a_config_package =
  classes().that().areAnnotatedWith(Configuration.class)
    .or().areNotNestedClasses().and().areAnnotatedWith(ConfigurationProperties.class)
    .should().resideInAPackage("..config..")
    .as("Configs should reside in a package '..config..'");
Örnek - Anotasyon ve Sınıf İsmi
Şöyle yaparız. Burada SpringBootApplication anotasyonuna sahip sınıfların isminin MainApp olması kontrol ediliyor.
@AnalyzeClasses(packages = "org.mypackage")

@ArchTest
static ArchRule app_class_name_should_be_app =
   classes().that().areAnnotatedWith(SpringBootApplication.class)
     .should().haveSimpleName("MainApp");
Örnek - Sınıf İsmi Son Ek
Şöyle yaparız Burada belirli bir paketteki sınıfların son ekinin Controller olması kontrol ediliyor. allowEmptyShould(true) ile eğer Controller sınıfı yoksa test başarısız olmuyor
@ArchTest
static ArchRule controllers_should_be_suffixed =
  classes()
    .that().resideInAPackage("..controller..")
    .or().areAnnotatedWith(RestController.class)
    .should().haveSimpleNameEndingWith("Controller")
    .allowEmptyShould(true);
fields metodu
Örnek
Şöyle yaparız
@ArchTest
private final ArchRule loggers_should_be_private_static_final =
  fields().that().haveRawType(Logger.class)
    .should().bePrivate()
    .andShould().beStatic()
    .andShould().beFinal()
    .because("we agreed on this convention");
noClasses metodu
Örnek
Şöyle yaparız
@ArchTest
static final ArchRule interfaces_should_not_have_names_ending_with_the_word_interface =
  noClasses()
  .that()
  .areInterfaces()
  .should()
  .haveNameMatching(".*Interface");
slices metodu
Örnek
Şöyle yaparız
@ArchTest
static final ArchRule no_cycles_by_method_calls_between_slices =
  slices()
  .matching("..(simplecycle).(*)..")
  .namingSlices("$2 of $1")
  .should()
  .beFreeOfCycles();
Açıklaması şöyle
This takes your code and slices it by package so the above is slicing the code by the packages directly below the simplecycle package and asserts that there are no cycles. If instead of the (*) we had (**) it would look at all sub-packages below simplecycle .
Layered Architecture Enforcement

Kendi Kuralımız
Bir örnek burada. Bu kurulu kullanmak için kuralın check() metodu çağrılır. Şöyle yaparız
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;

public class KinesisSerializableTest {

  @Test
  public void serializable_classes_should_have_valid_serialVersionUID() {
    String basePackage = KinesisSinks.class.getPackage().getName();
    JavaClasses classes = new ClassFileImporter()
                .withImportOption(onlyCurrentModule())
                .importPackages(basePackage);

    ArchUnitRules.SERIALIZABLE_SHOULD_HAVE_VALID_SERIAL_VERSION_UID.check(classes);
  }
}
@AnalyzeClasses Anotasyonu
importOptions Alanı
Örnek
Şöyle yaparız
// ignore tests
@AnalyzeClasses(packages = "org.mypackage", 
  importOptions = ImportOption.DoNotIncludeTests.class)

// ignore class
@AnalyzeClasses(packages = "org.mypackage", 
  importOptions = {ArchUnitTest.ExcludeControllerImportOption.class, 
                   ImportOption.DoNotIncludeTests.class})

public class ArchUnitTest {

  static class ExcludeControllerImportOption 
    implements com.tngtech.archunit.core.importer.ImportOption {
    @Override
    public boolean includes(Location location) {
      return !location.contains("SomeControllerClassThatNeedsToBeExcluded");
    }
}

Hexagonal Architecture
Örnek
Şöyle yaparız
@ArchTest
static final ArchRule onion_architecture_is_respected = onionArchitecture()
  .domainModels("..domain.model..")
  .domainServices("..domain.service..")
  .applicationServices("..application..")
  .adapter("cli", "..adapter.cli..")
  .adapter("persistence", "..adapter.persistence..")
  .adapter("rest", "..adapter.rest..");
Örnek
Kullanımı şöyle. Kaynak kod burada
HexagonalArchitecture.boundedContext("io.reflectoring.buckpal.account")
                     .withDomainLayer("domain")
                     .withAdaptersLayer("adapter")
                     .incoming("in.web")
                     .outgoing("out.persistence")
                     .and()
                         .withApplicationLayer("application")
                         .services("service")
                         .incomingPorts("port.in")
                         .outgoingPorts("port.out")
                     .and()
                         .withConfiguration("configuration")
                         .check(new ClassFileImporter()
                         .importPackages("io.reflectoring.buckpal.."));



Hiç yorum yok:

Yorum Gönder