아키텍처 테스트

이연중·2021년 2월 22일
0

JAVA

목록 보기
20/20

Archunit 소개


어플리케이션의 아키텍처를 테스트 할 수 있는 오픈 소스 라이브러리로 패키지, 클래스, 레이어, 슬라이스 간 의존성을 확인할 수 있는 기능을 제공한다.

아키텍처 테스트 유스 케이스(Circular Dependancy 확인)

  • circular dependency가 있으면 코드 분석시 일관된 흐름이 없어 코드 파악 및 분석이 어렵기에 다음의 과정들을 통해 이를 찾아내고 제거해야 함

  • A라는 패키지가 B (또는 C, D) 패키지에서만 사용되고 있는지 확인 가능

  • Service라는 이름의 클래스들이 Controller 또는 Service라는 이름의 클래스에서만 참조하고 있는지 확인

  • Service라는 이름의 클래스들이 ..service..라는 패키지에 들어있는지 확인

  • A라는 어노테이션을 선언한 메소드만 특정 패키지 또는 특정 어노테이션을 가진 클래스를 호출하고 있는지 확인

  • 특정 스타일의 아키텍처를 따르고 있는지 확인

ArchUnit 설치


JUnit5용 ArchUnit 설치

<dependency>
	<groupId>com.tngtech.archunut</groupId>
    <artifactId>archunit-junit5-engine</artifactId>
    <version>0.12.0</version>
    <scope>test</scope>
</dependency>

주요 사용법

  1. 특정 패키지에 해당하는 클래스를 바이트코드를 통해 읽어들이고
  2. 확인할 규칙을 정의하고
  3. 읽어들인 클래스들이 그 규칙을 잘 따르는지 확인
@Test
public void Services_should_only_be_accessed_by_Controllers(){
	JavaClasses importedClasses= new ClassFileImporter().importPackages("com.mycompany.myapp"); 
//확인할 클래스 읽어들임
    
    ArchRule myRule= classes()
        .that().resideInAPackage("..service..")
        .should().onlyBeAccessed().byAnyPackage("..controller..","..service..");
//룰 정의
    
    myRule.check(importedClasses);
//룰을 잘 따르는지 확인
}

JUnit5 확장팩 제공

  • @AnalyzeClasses: 클래스를 읽어들여 확인할 패키지 설정
  • @ArchTest: 확인할 규칙 정의

ArchUnit: 패키지 의존성 확인


확인하려는 패키지 구조(순환 참조 없는 상태)

확인

  • ..domain.. 패키지에 있는 클래스는 ..study.., ..member.., ..domain에서 참조 가능

  • ..member.. 패키지에 있는 클래스는 ..study..와 ..member..에서만 참조 가능

    반대로 ..domain.. 패키지는 ..member.. 패키지를 참조하지 못함

  • ..study.. 패키지에 있는 클래스는 ..study..에서만 참조 가능

  • 순환 참조 없어야 함

ArchUnit이 제공하는 API를 직접 사용해 테스트 코드 작성

public class ArchTests{
    @Test
    void packageDependencyTests(){
        JavaClasses classes= new ClassFileImporter().importPackages("me.always.archtest");
        
        ArchRule domainPackageRule= classes().that().resideInAPackage("..domain..")
            .should().onlyBeAccessed().byClassesThat().resideInAnyPackage("..study..",",,member..","..domain..");
        
        domainPackageRule.check(classes);
//domain이라는 키워드가 들어있는 패키지들은 study, member, domain이라는 키워드가 있는 패키지만 참조할 수 있다
        
        ArchRule memberPackageRule= noClasses().that().resideInAPackage("..domain..")
            .should().accessClassesThat().resideInAPackage("..member..");
        
        memberPackageRule.check(classes);
//domain이라는 키워드의 패키지 안에 있는 어떠한 클래스도 member라는 키워드를 가진 패키지 안에 있는 클래스에 접근할 수 없다
        
        ArchRule studyPackageRule= noClasses().that().resideOutsideOfPackage("..study..")
            .should().accessClassesThat().resideInAPackage("..study..");
        
        studyPackage.check(classes);
//study라는 키워드의 패키지에 study라는 키워드의 패키지가 아닌 다른 패키지가 접근할 수 없다
        
        ArchRule freeOfCycles= slices().matching("..archtest.(*)..")
            .should().beFreeOfCycles();
        
        freeOfCycles.check(classes);
//순환 참조 제거
    }
}

ArchUnit이 제공하는 JUnit 확장팩 사용

@AnalyzeClasses: 클래스를 읽어들여 확일할 패키지 설정

@Archtest: 확인할 규칙 정의

@AnalyzeClasses(packagesOf= App.class)
public class ArchTests{
    @ArchTest
    ArchRule domainPackageRule= classes().that().resideInAPackage("..domain..")
        .should().onlyBeAccessed().byClassesThat()
        .resideInAnyPackage("..study..","..member..","..domain..");
    
    @ArchTest
    ArchRule memberPackageRule= noClasses().that().resideAPackage("..domain..")
        .should().accessClassesThat().resideInAPackage("..member..");
    
    @ArchTest
    ArchRule studyPackageRule= noClasses().that().resideOutsideOfPackage("..study..")
        .should().accessClassesThat().resideInAnyPackage("..study..");
    
    @ArchTest
    ArchRule freeOfCycles= slices().matching("..archtest.(*)..")
        .should().beFreeOfCycles();
    
}

코드의 가독성은 좋아지나, 디스플레이 이름을 따로 줄 수는 없음

ArchUnit: 클래스 의존성 확인


확인하려는 클래스 의존성

테스트 내용

  • StudyController는 StudyService와 StudyRepository를 사용할 수 있음
  • Study로 시작하는 클래스는 ..study.. 패키지에 있어야 함
  • StudyRepository는 StudyService와 StudyController를 사용할 수 없음
@AnalyzeClasses(packageOf= App.class)
public class ArchClassTests{
	@ArchTest
	ArchRule controllerClassRule= 	classes().that().haveSimpleNameEndingWith("Controller")
    .should().accessClassesThat().haveSimpleNameEndingWith("Service")
    .orShould().accessClassesThat().haveSimpleEndingWith("Repository");  
//이름이 Controller로 끝나는 클래스들은 이름이 Service와 Repository로 끝나는 클래스들을 참조할 수 있다. 
    
    @ArchTest
    ArchRule repositoryClassRule= noClasses().that().haveSimpleEndingWith("Repository")
        .should().accessClassesThat().haveSimpleNameEndingWith("Service");
//Repository로 끝나는 클래스들은 Service로 끝나는 클래스들을 사용할 수 없다
    
   @ArchTest
    ArchRule studyClassesRule= classes().that().javeSimpleNameStartingWith("Study")
        .and().areNotEnums()
        .and().areNotAnnotatedWith(Entity.class)
        .should().resideInAnyPackage("..study..");
//Study로 시작하는 클래스이고, enum이 아니고 entity라는 어노테이션을 가지고 있지 않은 클래스들은 반드시 Study 패키지 안에 있어야 한다. 
}

참고

https://www.inflearn.com/course/the-java-application-test

profile
Always's Archives

0개의 댓글