인프런 커뮤니티 질문&답변

감바스님의 프로필 이미지
감바스

작성한 질문수

Practical Testing: 실용적인 테스트 가이드

@Mock, @Spy, @InjectMocks

안녕하세요 @Autowired, @Mockbean, @Mock, @InjectMocks에 대해 질문 있습니다.

해결된 질문

작성

·

87

0

  1. 각 어노테이션을 언제 주로 사용하는지에 대해 아래와 같이 정리를 했는데 맞게 정리 한 건지 궁금합니다.

@Autowired를 사용하는 경우

@ActiveProfiles("test")
@SpringBootTest
class OrderServiceTest {

    @Autowired
    private ProductRepository productRepository;
 
    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private StockRepository stockRepository;

    @Autowired
    private OrderService orderService;

    ...
}

스프링 컨텍스트에 실제 빈 객체를 등록하고 해당 빈을 사용합니다. 이렇게 실제 빈을 사용하는 테스트라 실제 동작 검증에 있어서 가장 정확성이 높은 테스트 입니다.

@Mockbean과 @Autowired를 섞어 사용하는 경우

@WebMvcTest(controllers = ProductController.class)
class ProductControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @MockBean
    private ProductService productService;

    ...
}

스프링 컨텍스트에 실제 빈이 아닌 Mock 빈을 등록합니다.

컨트롤러 레이어에 대해서만 단위 테스트를 하고 싶은데 컨트롤러가 서비스 레이어에 의존하고 있으니 이러한 의존을 끊기 위해 @MockBean을 사용하는 방식입니다.

@Mock과 @InjectMocks

@ExtendWith(MockitoExtension.class)
class MailServiceTest {

    @Mock
    private MailSendClient mailSendClient;

    @Mock
    private MailSendHistoryRepository mailSendHistoryRepository;

    @InjectMocks
    private MailService mailService;

    ...
}

@Mock을 통해 스프링 컨테이너에 등록되지 않는 가짜 객체를 생성합니다. 그리고 @InjectMocks에 사용된 객체에 @Mock을 통해 생성한 가짜 객체를 주입하는 방식입니다.

스프링 컨테이너가 필요없는 외부 시스템에 대해 테스트를 할때 진행하는 방식입니다.

  1. 위 질문 중에 "@Mockbean과 @Autowired를 섞어 사용하는 경우" 코드에 대해 질문이 있습니다. 컨트롤러에 대한 단위 테스트를 하기 위해 @MockBean을 사용하여 서비스에 대한 의존성을 끊는 부분은 이해를 했습니다. 근데 결국 서비스 객체를 가져다 쓰니 서비스가 의존하고 있는 Repository에 대한 부분도 @MockBean을 사용하여 스프링 컨테이너에 Mock을 등록해야 하지 않나 라는 생각이 듭니다.

  2. 아래 코드에 대해 질문이 있습니다. 실제 스프링 부트를 실행 하면 각 서비스 객체와 Repository 객체 모두 스프링 컨테이너에 등록되어 서비스 레이어쪽에서 Repository레이어에 의존하는 상황입니다. 저는 스프링 컨테이너에 빈들도 잘 등록되고 서로 잘 데이터를 주고 받는지도 테스트를 해야한다고 생각하는데 아래와 같이 @InjectMocks과 @Mock을 사용하면 스프링 컨테이너와 상관이 없어져서 아래와 같은 상황에서 @InjectMocks과 @Mock을 사용하여 테스트 코드를 작성해도 되는지 궁금합니다.

@ExtendWith(MockitoExtension.class)
class CustomServiceImplTest {

    @InjectMocks
    private CustomServiceImpl customServiceImpl;

    @Mock
    private BankRepository bankRepository;

    @Mock
    private ProductRepository productRepository;

    @Mock
    private OrderRepository orderRepository;

    ...
}
  1. 1번 질문과 같이 정리를 했지만 @Autowired와 @Mockbean 사용에 대해 헷갈리는점이 있어 아래 내용을 정리 했는데 맞게 이해를 한건지 궁금합니다.
    @Autowired를 테스트 코드에서 사용하는 핵심은 해당 기능을 스프링 컨테이너에 등록하는걸 넘어서 해당 객체의 기능을 실제 사용하겠다 라는 의미다.
    @Mockbean을 테스트 코드에서 사용하는 핵심 이유는 의존 관계를 끊기 위함이다. 예를 들어 컨트롤러 쪽에서 서비스쪽에 강한 의존관계를 가지고 있어 우선 @Mockbean을 통해 생성한 가짜 객체를 스프링 컨테이너에 등록하고 이 가짜 객체를 컨트롤러쪽에서 의존하고 있는 객체에 넣어줘서 의존관계를 끊는다. 주의할점은 @Mockbean을 통해 생성한 가짜 객체의 기능은 사용하지 않는다.

     

  2. 아래 코드에서 @MockBean을 통해 생성한 ProductService 객체는 가짜 객체이므로 ProductRepository 객체를 못 불러오나요?

@WebMvcTest(controllers = ProductController.class)
class ProductControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @MockBean
    private ProductService productService;
    ...
}

 

답변 1

0

박우빈님의 프로필 이미지
박우빈
지식공유자

안녕하세요, 감바스 님!

 

1. 각 어노테이션을 언제 주로 사용하는지에 대해 아래와 같이 정리를 했는데 맞게 정리 한 건지 궁금합니다.

네 잘 정리해 주셨네요 ㅎㅎ

 

2. 위 질문 중에 "@Mockbean과 @Autowired를 섞어 사용하는 경우" 코드에 대해 질문이 있습니다. 컨트롤러에 대한 단위 테스트를 하기 위해 @MockBean을 사용하여 서비스에 대한 의존성을 끊는 부분은 이해를 했습니다. 근데 결국 서비스 객체를 가져다 쓰니 서비스가 의존하고 있는 Repository에 대한 부분도 @MockBean을 사용하여 스프링 컨테이너에 Mock을 등록해야 하지 않나 라는 생각이 듭니다.

서비스를 mocking 했으니 애초에 Controller는 Repository에 접근할 수가 없습니다.
따라서 굳이 Repository까지 MockBean으로 등록하지 않아도 되는거죠.

실제로 서비스를 호출하는 부분과 Repository를 호출하는 부분에 디버거를 걸고 테스트를 실행시켜 보세요 :)

 

3. 아래 코드에 대해 질문이 있습니다. 실제 스프링 부트를 실행 하면 각 서비스 객체와 Repository 객체 모두 스프링 컨테이너에 등록되어 서비스 레이어쪽에서 Repository레이어에 의존하는 상황입니다. 저는 스프링 컨테이너에 빈들도 잘 등록되고 서로 잘 데이터를 주고 받는지도 테스트를 해야한다고 생각하는데 아래와 같이 @InjectMocks과 @Mock을 사용하면 스프링 컨테이너와 상관이 없어져서 아래와 같은 상황에서 @InjectMocks과 @Mock을 사용하여 테스트 코드를 작성해도 되는지 궁금합니다.

그래서 제시해주신 코드는 스프링과 무관한 단위 테스트를 작성할 때 사용하게 되고요.
스프링 통합 테스트를 진행할 때에는 MockBean을 사용하게 됩니다.

 

4. 1번 질문과 같이 정리를 했지만 @Autowired와 @Mockbean 사용에 대해 헷갈리는점이 있어 아래 내용을 정리 했는데 맞게 이해를 한건지 궁금합니다.
@Autowired를 테스트 코드에서 사용하는 핵심은 해당 기능을 스프링 컨테이너에 등록하는걸 넘어서 해당 객체의 기능을 실제 사용하겠다 라는 의미다.
@Mockbean을 테스트 코드에서 사용하는 핵심 이유는 의존 관계를 끊기 위함이다. 예를 들어 컨트롤러 쪽에서 서비스쪽에 강한 의존관계를 가지고 있어 우선 @Mockbean을 통해 생성한 가짜 객체를 스프링 컨테이너에 등록하고 이 가짜 객체를 컨트롤러쪽에서 의존하고 있는 객체에 넣어줘서 의존관계를 끊는다. 주의할점은 @Mockbean을 통해 생성한 가짜 객체의 기능은 사용하지 않는다.

 가짜 객체의 기능을 사용하지 않는다기 보다는, 테스트 시 프로덕션 코드에서 가짜 객체를 호출까지는 하나, 임의로 지정한 반환값을 돌려줍니다.

 

5. 아래 코드에서 @MockBean을 통해 생성한 ProductService 객체는 가짜 객체이므로 ProductRepository 객체를 못 불러오나요?

Mock 객체는 기본적으로 실제 객체를 모방한 껍데기 입니다.
우리가 mocking을 통해서 행위를 마음대로 지정할 수 있는 것도 그 때문이죠.
따라서 Mock 객체는 ProductRepository를 가지고 있지 않습니다. ProductService의 모습을 흉내만 내고 있는 것이죠.

 

위에서 말씀드린 것처럼, 실제 디버거를 켜고 mock 객체가 어떻게 생겼는지 살펴보시길 바라요.
도움이 되셨기를 바랍니다.
감사합니다 🙂

감바스님의 프로필 이미지
감바스

작성한 질문수

질문하기