지난 시간 테스트 코드에 대해서 조금 알아보았다.
진행을 하다가 이런 의문이 들었다.
클래스간의 의존성은 어떻게 해결하지?
예를 들어 Controller를 테스트하려면 주입받아온 Service, 또 거기서 주입받아 온 Repository는 또 설계해 줘야하나?
이럴 때, 필요한 객체가 Mock객체, 가짜 객체이다.
흔히 목업자료할 때 쓰이는 그 단어.
Mockito
귀여운 네이밍 센스의 뫀히토 라는 Mockito framework: Mock객체를 통해 이를 쉽게 해준다고 합니다.
그냥 된다라기보단 어떤 것으로 부터 기능을 제공받고 있는 지를 아는 것이 조금은 중요한 듯 해서 참고차 기록해둡니다.
사용문법
@ExtendWith(MockitoExtension.class) // @Mock 사용을 위해 설정합니다.
class ~~~Test {
...
맨 처음으로 mock을 사용하겠다라는 것을 명시합니다.
@Mock
ProductRepository productRepository;
@Mock
FolderRepository folderRepository;
그리고 빈을 주입받아와야할 곳에 @Mock을 넣어줍니다.
그렇게 되면 위처럼 실제로 주입받았었던 Repository를 가져오는 것이 아닌 가짜 Mock객체를 넣어줍니다.
실제 DB가 실행이 되는 것은 아니지만 Service객체가 잘 작동하는 지 테스트를 위한 Mock객체인 셈.
그러면 같은 클래스명과 메소드명으로 테스트를 진행할 수는 있지만 실제 데이터가 없는데 어떻게 테스트가 진행될 수가 있을까?
// when
ProductResponseDto result = productService.updateProduct(productId, requestMyPriceDto);
우선 테스트 실행부에 있는 코드. productService mock객체가 가지고 있는 updateProduct메소드를 테스트 해야한다.
updateProduct 메소드를 살펴보자.
public ProductResponseDto updateProduct(Long id, ProductMypriceRequestDto requestDto) {
int myprice = requestDto.getMyprice();
if (myprice < MIN_MY_PRICE) {
throw new IllegalArgumentException("유효하지 않은 관심 가격입니다. 최소 " + MIN_MY_PRICE + "원 이상으로 설정해주세요.");
}
Product product = productRepository.findById(id).orElseThrow(() ->
new NullPointerException("해당 상품을 찾을 수 없습니다.")
);
product.update(requestDto);
return new ProductResponseDto(product);
}
코드 중에서 아래 부분에서 문제가 있다.
Product product = productRepository.findById(id).orElseThrow(() ...
productRepository에서 product를 찾아와야 하는데 mock객체라서 product를 가져올 수 없다.
이 부분은 우리가 테스트해야할 부분이 아니기 때문에 그냥 product를 하나 만들어서 넣어 줌으로써
우리가 진짜로 테스트해야하는 update 기능이 잘 되는지 확인해야한다.
그리고 이렇게 product를 넣어주는 걸 사용케이스 추가라고 한다.
사용케이스 추가
product를 productRepository에서 찾아올 수 없어서 오류가 발생한다.
Mock객체를 통해서 메소드가 온전히 실행만 되게 했지, 실제로 모든 데이터를 mock객체의 역할을 하던 실제객체에서 가져올 수 없기 때문이다.
User user = new User();
ProductRequestDto requestProductDto = new ProductRequestDto(
"닌텐도 스위치",
"https://~~.jpg",
"https://search.~~",
351500
);
Product product = new Product(requestProductDto, user);
위처럼 직접 하드코딩해서 product객체 하나를 만들어준다.
그러면 productRepository에서 원래 찾아왔어야 했는데, mock객체인 productRepository에서 가져온 것처럼 해주는 원리.?
그리고 중요한 것은 우리가 준비한 사용케이스를 사용하게끔 설정해두는 코드를 아래와 같이 넣어준다.
given(productRepository.findById(productId)).willReturn(Optional.of(product));
productRepository.findById(productId)에 대한 리턴을 우리가 만들어둔 Optional.of(product)으로 해주겠다는 의미이다.
이렇게 되면 updateProduct메소드가 실행되는 도중에 productRepository.findById(productId)가 mockRepository이므로 제대로 된 데이터를 찾아와 줄 수 없는것이 아니라 우리가 준비해둔 product를 리턴해줄 수가 있다.
뭔 말인지 알겠는데, 어떻게 하라는 건지 모르겠다.
(공부하겠다라는 뜻)
'TIL : Today I learned (or Week)' 카테고리의 다른 글
TIL 230717 : TDD? BDD? (0) | 2023.07.18 |
---|---|
WIL 230716 : 테스트 코드는 또 뭐냐 (0) | 2023.07.18 |
TIL 230713 : 테스트코드..? (0) | 2023.07.17 |
TIL 230710 : 다중 JSON 다루기 (JSONObject, JSONArray) (1) | 2023.07.10 |
WIL 230709 : 스프링를 훑어 보며 (0) | 2023.07.09 |