ArchUnit
- 아키텍처 테스트 유틸리티 (JUnit 5 지원)
- https://www.archunit.org/
<!-- https://mvnrepository.com/artifact/com.tngtech.archunit/archunit -->
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit-junit5</artifactId>
<version>0.13.1</version>
<scope>test</scope>
</dependency>
패키지 정리
- 인프라 패키지에서 모듈 패키지 사용하지 않기.
- Study 패키지에 있는 클래스는 Event와 Study에 들어있는 클래스에서만 사용한다.
- Event 패키지에 있는 클래스는 Study와 Account 그리고 Event 패키지에 들어있는 클래스를 사용한다.
- ...
- 모듈 간에 순환 참조가 없는지 확인한다.
infra에 있는 패키지에서 modules패키지를 사용하지 않게 리팩토링 한다.
package me.weekbelt.studyolle.infra.config;
@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService; // 수정
private final DataSource dataSource;
@Override
protected void configure(HttpSecurity http) throws Exception {
// ..........
http.rememberMe()
.userDetailsService(userDetailsService) // 수정
.tokenRepository(tokenRepository())
;
}
// .........
}
패키지에서 위에 구조처럼 참조를 하는지 테스트하기 위한 테스트 코드를 작성한다.
Study 도메인은 Study와 Event에서만 참조가 가능해야한다. 그런지 검증하는 테스트 코드를 작성한다.
package me.weekbelt.studyolle;
// .......
@AnalyzeClasses(packagesOf = App.class)
public class PackageDependencyTests {
private static final String STUDY = "..modules.study..";
private static final String EVENT = "..modules.event..";
private static final String ACCOUNT = "..modules.account..";
private static final String TAG = "..modules.tag..";
private static final String ZONE = "..modules.zone..";
@ArchTest
ArchRule studyPackageRule = classes().that().resideInAPackage("..modules.study..")
.should().onlyBeAccessed().byClassesThat()
.resideInAnyPackage(STUDY, EVENT);
}
테스트를 돌려보니 실패가 나온다.
Acount엔티티에 isManagerOf메소드가 Study를 참조하고 있다고 에러메시지가 떴다.
package me.weekbelt.studyolle.modules.account;
@Builder @AllArgsConstructor @NoArgsConstructor
@Getter @Setter @EqualsAndHashCode(of = "id")
@Entity
public class Account {
// ......
// 삭제
public boolean isManagerOf(Study study) {
return study.getManagers().contains(this);
}
}
isManagerOf 메소드를 사용하는 곳을 찾는다.
package me.weekbelt.studyolle.modules.study;
@RequiredArgsConstructor
@Transactional
@Service
public class StudyService {
// ..........
private void checkIfManager(Account account, Study study) {
if (!study.isManagedBy(account)) { // account.isManager(study)를 study.isManageredBy(account)로 수정한다.
throw new AccessDeniedException("해당 기능을 사용할 수 없습니다.");
}
}
// .........
}
Study 엔티티에 isManageredBy메소드를 생성한다.
package me.weekbelt.studyolle.modules.study;
// ......
public class Study {
// .........
public boolean isManagedBy(Account account) {
return this.getManagers().contains(account);
}
// ..........
}
나머지 패키지를 검증하는 테스트코드도 작성한다.
package me.weekbelt.studyolle;
@AnalyzeClasses(packagesOf = App.class)
public class PackageDependencyTests {
// ........
@ArchTest
ArchRule eventPackageRule = classes().that().resideInAPackage(EVENT)
.should().accessClassesThat()
.resideInAnyPackage(STUDY, ACCOUNT, EVENT);
@ArchTest
ArchRule accountPackageRule = classes().that().resideInAPackage(ACCOUNT)
.should().accessClassesThat().resideInAnyPackage(TAG, ZONE, ACCOUNT);
}
그 다음 순환 참조가 없는지 검증하는 테스트를 작성한다.
package me.weekbelt.studyolle;
@AnalyzeClasses(packagesOf = App.class)
public class PackageDependencyTests {
// .....
@ArchTest
ArchRule cycleCheck = slices().matching("me.weekbelt.studyolle.modules.(*)..")
.should().beFreeOfCycles();
}
순환 참조가 발생했단는 에러가 떴다.
settings패키지에 있는 요소들을 account 패키지로 이동시킨다.
마지막으로 modules패키지는 moduels패키지내에서만 사용할 수 있는지 확인하는 테스트 추가
package me.weekbelt.studyolle;
@AnalyzeClasses(packagesOf = App.class)
public class PackageDependencyTests {
// ........
@ArchTest
ArchRule modulesPackageRule = classes().that().resideInAPackage("me.weekbelt.studyolle.modules..")
.should().onlyBeAccessed().byClassesThat()
.resideInAnyPackage("me.weekbelt.studyolle.modules..");
// ..........
}
패키지 이동시킨다.
참고: https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-JPA-%EC%9B%B9%EC%95%B1#
'스프링과 JPA 기반 웹 어플리케이션 개발 > 6부 패키지와 테스트 정리' 카테고리의 다른 글
70. 테스트 클래스 정리 (0) | 2020.05.06 |
---|