1. Service 클래스는 Repository 클래스를 의존한다.

아래 코드에서 보면, ReviewService
ReviewRepository, OrdersRepository, ProductRepository를 의존하고 있다.

ReviewService.java

@Service
@RequiredArgsConstructor
public class ReviewService {
	private final ReviewRepository reviewRepository;
  private final OrdersRepository ordersRepository;
  private final ProductRepository productRepository;

  
  @Transactional
  public ResponseEntity<ReviewResponseDto> createReview(ReviewRequestDto requestDto, User user) {
    Orders order = ordersRepository.findById(requestDto.getOrderId())
            .orElseThrow(() -> new NullPointerException("해당하는 주문이 존재하지 않습니다"));

    if (!order.getUser().equals(user)) {
        throw new IllegalArgumentException("작성 권한이 없는 유저 입니다.");
    }

    Review review = reviewRepository.save(new Review(requestDto,order));
    return ResponseEntity
            .status(HttpStatus.CREATED.value())
            .body(new ReviewResponseDto(reviewRepository.save(review)));
  }

}

즉, springbootTest 에서 테스트를 하는 것이 아닌, Service 클래스 하나를 단위 테스트를 하는 경우, ReviewService 클래스 인스턴스가 생성 되려면

코드에 선언된 3개의 레포지토리 클래스가 필요하다.

2. Service의 테스트는 ‘값’이 그다지 중요하지 않다.

모듈 테스트의 목적은 어떤 입력이 올 때,

원하는 값이 return 되는지, 원하는 횟수만큼 내부 로직이 동작 되는지 확인을 하는 것이다.

즉, Service에 사용되는 내부 동작 Entity나 Dto를 디테일은 케이스에 맞춰서 일일하게 만들 필요가 없다.

전체 코드

@Mock
ReviewRepository reviewRepository;

@Mock
OrdersRepository ordersRepository;

@Mock
ProductRepository productRepository;

@Mock
Orders orders;

private String comment;
private Integer rating;

@BeforeEach
void setup() {
    comment = "여기에 댓글 내용.";
    rating = 3;
}

@Test
@DisplayName("[201] 성공 케이스")
void createReview() {
		// 여기서 가짜 Mock class를 Inject 해준다.    
		ReviewService reviewService = new ReviewService(reviewRepository, ordersRepository, productRepository);
    
		//given -> 답정너 값 넣기
    ReviewRequestDto requestDto = new ReviewRequestDto(1L, comment, rating);
    Review review = new Review(comment, rating); // 1
    Optional<Orders> optionalOrders = Optional.of(orders); //2
		User user = new User(); //3
    
		// when -> 답정너 대답 넣기
    when(reviewRepository.save(any(Review.class))).thenReturn(review); // 1 
    when(ordersRepository.findById(any(Long.class))).thenReturn(optionalOrders); // 2
    when(orders.getUser()).thenReturn(user); // 3

    //when(orders.getUser().equals(user)).thenReturn(true); // premitive type return은 when을 사용할 수 없다.

    //then
    ResponseEntity<ReviewResponseDto> result = reviewService.createReview(requestDto, user);
    assertThat(result.getBody().getComment()).isEqualTo(review.getComment());
    assertThat(result.getBody().getRating()).isEqualTo(review.getRating());
    assertThat(result.getStatusCode()).isEqualTo(HttpStatus.CREATED);
}

2-1. Mockito는 가짜 의존성을 만들어 준다.

@Mock
ReviewRepository reviewRepository;

@Mock
OrdersRepository ordersRepository;

@Mock
ProductRepository productRepository;

//...(생략)

@Test
@DisplayName("[201] 성공 케이스")
void createReview() {
	// 여기서 가짜 Mock class를 Inject 해준다.    
	ReviewService reviewService = new ReviewService(reviewRepository, ordersRepository, productRepository);
  // ... 생략
}

2-2. given: 클래스에 미리 선언해둔 Mock 객체를 이용해서 가짜 값을 만들어준다.

<aside> 💡 given을 넣는 이유는 service 내부 동작 로직을 확인하기 위해서다.

</aside>

<aside> 💡 이런 given이 주어질 때, Service 함수는 이런 동작을 할거다~~ 가정을 하면서 given을 넣어줘야 한다.

</aside>

@Test
@DisplayName("[201] 성공 케이스")
void createReview(){
 // ... 생략
 //given -> 답정너 값 넣기
  ReviewRequestDto requestDto = new ReviewRequestDto(1L, comment, rating);
  Review review = new Review(comment, rating); // 1
  Optional<Orders> optionalOrders = Optional.of(orders); //2
  User user = new User(); //3
  // 생략 
}

2-3. when : 클래스에 미리 선언해둔 Mock 객체를 사용하는 가짜 결과를 만들어준다.