공부내용공유

Junit 5 Service,Mapstruct, ModelMapper Unit Test (단위 테스트) 본문

Spring/Spring

Junit 5 Service,Mapstruct, ModelMapper Unit Test (단위 테스트)

forfun 2023. 7. 7. 14:39

서론


 

현재 진행중인 프로젝트에서 잘만 돌아가던 API 가 안된다고 프론트 팀원분에게 연락이 왔고 나는 로그를 보고 오류를 확인을 했는데 다름이 아닌 다른 파트를 맡은 친구가 자신의 파트에서 기능 일부분을 수정했고 자신의 파트만 되는지 시행해 보고 PR 을 넣었던 것이다.

 

나도 그냥 기능을 수정했나 보다라고 간과하고 PR을 받았고 그 결과 내가 만들었던 API가 영향을 받아 오류가 났던것이다.

이를 경험하고 Test Code 의 중요성을 깨닫고 PR 을 넣을때는 적어도 Unit Test 는 모든 파트에서 돌려보고 넣는게 맞겠구나 라는 생각이 들어 Test Code 를 작성하게 되었다.

본론


Service 계층에서의 Test Code 를 먼저 작성하였다.

이 글에서는 내가 진행한 프로젝트에서 Junit 5 를 통해 어떤식으로 테스팅을 했는지 정리할것이다.

여러 레퍼런스를 찾아보면서 작성을 하였지만 부족한 부분이 많고 추후 책이나 강의등을 통해 제대로 공부하고 다시 리팩토링을 할 예정이다.

 

서비스 계층은 Controller 계층과 Repository 계층과 연결되어져 있고 그중 Repository 계층에는 의존을 하면서 Repository의 메서드를 사용한다.

 

그리고 이번 프로젝트에서 나는 mapping을 위해 MapStruct 와 ModelMapper 를 서비스 계층에서 사용하였다.

여기서 이제 Test Double 을 사용하여 Mock 할것과 Spy 할것 등을 정하고 테스트를 만들어야 한다.

  • Repository - Mock
  • modelMapper - Mock
  • mapStruct - Spy

여기서 왜model mapper 는 mock을 하고 maprStruct는 spy를 한 이유는

mapStruct의 경우는 각 mapper 마다 내가 어떻게 로직을 설정하냐에 따라 결과가 달라지기 때문이다.

MapStrcut Test

예를들면 Lecture 와 User 를 사용해서 특정 DTO를 만들때 mapStruct 를 사용한다 가정하자.

 

이때 두 객체에 같은 이름을 가진 level 이라는 필드가 있다면 내가 어떤 DTO를 만드냐에 따라 user 의 level 필드가 사용되어야 할때가 있고 Lecture의 level 필드가 사용되어야 할 때가 있다.

 

이러한 경우에 내가 mapStruct interface를 설정을 알맞게 해야 원하는 DTO가 만들어지고 이는 서비스 계층에서 굉장히 중요한 일중 하나라고 생각이 들어 mapStruct는 spy 로 받아 직접 mapStruct 만 테스트를 하였다.

 

이 경우는 굉장히 단순한 경우이지만 추후 복잡한 mapStruct 테스트를 만들게 되면 따로 정리 할예정이다.

 

그 이후 서비스의 한 메서드인 createLectureContent를 테스트를 하였다.

위 테스트에서 이미 mapStruct의 테스트는 진행했으므로 여기서는 given 을 통해 작동하지 않게 하였다.

 

ModelMapper

위 사진에서 보면modelmapper 는 Mock으로 설정을 하였다.

mapStruct는 위 설명과 같이 다양한 설정과 다양한 결과가 나오기 때문에 직접 Test 를 작성했지만

ModelMapper의 경우는 한 설정으로 계속 같은 방식으로 사용하기 때문에 테스트를 할 필요가 없다 생각이 들어 Mock으로 작성해 주었다.

그 후 doNothing 을 통해 행동을 제한해 주었다. (Stubber 방식 사용)

원래는 일관성을 위해 onGoingStubbing 을 사용하고 싶었으나 ModelMapper.map() 은 return 타입이 void였고 이는 onGoingStubbing에서 받지 않아서 Stubber 방식을 사용하였다.

 

Stubber란? OnGoingStubbing 이란?

 

BeforeAll, BeforeEach

테스트 진행을 위해 필요한 객체가 있을 수 있다.

BeforeAll 이나 BeforeEach 어노테이션을 사용하여 각 테스트를 위해 객체를 준비할 수 있는데

나는 필요할때만 객체를 사용하고 싶어서 메서드로 만들고 필요할때 사용하는 식으로 테스트를 작성하였다.

결론


처음 Service 계층 테스트 코드를 작성하면서 많은 고민을 하고 자료를 찾아 보았다.

  • 어느정도의 세부적인 단위로 나눠서 테스트를 해야하는지
  • 외부 유틸리티 기능들은 Spy를 활용할지 Mock을 활용할지
  • Exception은 어떤 방식으로 테스트를 해야하는지

추후 책이나 강의를 통해 좀 더 잘 공부하고 테스트 코드를 리팩토리 할 예정이다.