TroubleShooting & Study/SpringBoot

[Test] DIP 적용시켜서 테스트 코드 시간 단축시키기 - 실전편

DH_0518 2024. 7. 25. 16:04

이번에는 UserService, AuthService에 DIP를 적용시켜, 기존에 작성했던 AuthServiceTest, RestoreAuthTest, UserServiceTest의 시간을 단축시켜보고자 한다

 

https://kdh0518.tistory.com/entry/Architecture-Layered-Architecture-%ED%83%88%EC%B6%9C%EA%B8%B0-feat-SOLID-Hexagonal-DDD-Test-Code

 

[Architecture] Layered Architecture 탈출기 (feat. Hexagonal, Test Code)

개발을 시작하며 기본적인 MVC 패턴과 Layered 아키텍처를 공부한 이후 줄곧 Layered로 백엔드 패키지 구조를 설계했었다. 내가 지금까지 진행했던 프로젝트들은 대부분 데드라인이 있어서 일정 기

kdh0518.tistory.com

 

이론 편은 이전 글을 참고하자.

 

 

 

 

 

 

 

현재 존재하는 테스트 코드의 실행 시간은 다음과 같다

여기서 밑줄 친 테스트 외에는 모두 DIP가 적용되었거나, 단순히 event 발행만을 확인하는 테스트여서 시간이 오래 걸리지 않는다. 따라서 밑줄친 세 개의 테스트에서 효과를 볼 수 있는데, 그중에서 가장 시간이 오래 걸리는 AuthServiceTest를 리팩토링 해보겠다.

 

 

 

 

 

 

DIP 적용

 

 

먼저 Service 쪽에 Repository를 생성시켜서 의존성 방향을 바꿔야 한다.

기존 패키지 구조

 

 

1. Application에서 Port 생성

기존의 application 패키지에는 Service Interface와 구현체만이 존재한다. 여기서 Port를 만들어서 infrastructure에서 Port를 사용하도록 바꿔서, Service -> Repository에서 Service(Port) <- Repository로 의존성 방향을 바꾸겠다

기존 코드에서 in port(Service Interface)를 이미 사용하고 있었으므로, in과 out으로 port를 나눈 후 in에는 기존 Service Interface를, out에는 새로운 Repository Interface를 생성한다

 

 

 

2. Infrastructure에서 Jpa Repository, QueryDsl, 구현체를 분리

다음으로 Infrastucture에서 Jpa Repository와 QueryDsl Repository를 분리하고, Application Out Port의 구현체를 생성한다

수정된 Infrastructure 패키지

 

 

 

3. Application에서 JpaRepository가 아니라 Port의 Repository를 사용하도록 변경

이제 패키지 구조는 정리가 되었으니 실제로 Service의 구현체에서 Port의 Repository를 사용하도록 코드를 수정하여 의존성 방향을 바꿔보겠다

상위 모듈이 하위 모듈에 의존하고있는 기존 코드

 

상위 모듈이 하위 모듈에 의존하지 않는 수정된 코드

 

 

 

 

4. Infrastructure에서 JpaRepository가 아니라 Port의 Repository를 사용하도록 변경

3번과 마찬가지로 Infrastructure에서 JpaRepository가 아니라 Port의 Repository를 사용하도록 변경하여 의존성 방향을 바꿔주겠다. 이때 JpaRepository나 QueryDsl Repository는 Composition 하여 사용하면 된다

의존성 방향이 Repository -> Service로 변경된 코드

 

 

5. DIP 적용 후, 이상 없는지 Test

이제 DIP 적용을 완료했으니, 기존의 Test Code를 실행시켜서 이상 없는지 확인해 보자

 

성공!!!

 

 

 

 

 

 

 

Test Code 리팩토링

 

마지막으로 Test Code의 시간을 단축시키기 위해 모든 Service와 Repository를 주입받아 테스트하던 대형 테스트를, fake 객체를 생성하여 Unit Test로 바꿔보겠다

엄청난 의존성 주입이 필요한 지옥의 코드... 진짜 넌 지옥에 가라

 

 

 

의존성 주입이 필요한 객체들
수정된 테스트 코드. 직접 fake객체를 모두 주입시켜줬다. 이것도 지옥이네..

 

수정된 테스트 코드를 보면, 테스트에 필요한 모든 객체에 Fake객체를 주입시켜 주었다. AuthService 자체가 거대하다 보니, 너무 많은 객체가 필요해서 Fake 객체도 많이 만들어야 했고, util 객체(UuidGenerator, ModelMapper)의 경우는 Application Context로 가져올 수밖에 없었다.

 

저렇게 하지 않으려면 AuthService를 통째로 다 수정해야 하는데.. 그것까지는 너무 낭비인 것 같아서 힘들더라도 Fake객체를 모두 만들어서 넣어주었다.

 

그 결과는?

 

Test1. 회원가입 성공

  • 변경 전 : 1sec 164ms
  • 변경 후 : 272ms

변경 전 테스트 시간

 

변경 후 테스트 시간

 

첫 번째 테스트에서는 1164ms에서 272ms로 무려 4배가량 시간이 단축되었다

 

 

 

 

Test2. 펫 생성에 실패하더라도 회원가입은 성공

  • 변경 전 : 860ms
  • 변경 후 : 265ms

 

변경 전 테스트 시간

 

변경 후 테스트 시간

 

두 번째 테스트도 860ms -> 265ms로 시간이 확 줄어들었다. 이제 다른 테스트도 모두 작성 후 비교를 해보자

 

 

전체 Test

  • 변경 전 : 870 + 189 + 158 + 250 = 1467 ms
  • 변경 후 : 545 ms

922ms 감소, 대략 62.85%가 증가하였다 !!

 

(비록 AuthService가 너무 많은 클래스에 의존하고 있어서 Fake 객체를 많이 만들어줘야 했지만, DIP 적용과 유닛테스트로 실행 시간을 단축시킬 수 있다는 걸 직접 확인했다는데 의의를 두자)

변경 전 테스트 시간

 

변경 후 테스트 시간