Mockito기반의 단위 테스트

Seung jun Cha·2022년 8월 1일
0

1. 개념

  • 메소드의 실제 내부 동작은 실행되지 않고 상황 설정(Stub)
    만 할 수 있도록 해결한 것이 Test Double이다. Mockito는 이러한 Test Double을 지원하기 위해, 개발자가 동작을 직접 제어할 수 있는 가짜(Mock) 객체를 지원하는 테스트 프레임워크이다. 일반적으로 Spring으로 웹 애플리케이션을 개발하면, 여러 객체들 간의 의존성이 생긴다. 이러한 의존성은 단위 테스트를 작성을 어렵게 하는데, 이를 해결하기 위해 가짜 객체를 주입시켜주는 Mockito 라이브러리를 활용할 수 있다.

2. 사용법

2-1 Mock 의존성 주입

Mockito에서 Mock(가짜) 객체의 의존성 주입을 위해서는 크게 3가지 어노테이션이 사용된다.

  1. @Mock: Mock 객체를 만들어 반환해주는 어노테이션
  2. @Spy: Stub하지 않고 실제 메서드의 기능을 그대로 사용하는 어노테이션 (실제 인스턴스를 사용하여 mocking)
    ex) private PasswordEncoder passwordEncoder;
  3. @InjectMocks: @Mock 또는 @Spy로 생성된 가짜 객체를 자동으로 주입시켜주는 어노테이션
@ExtendWith(MockitoExtension.class)
class BoardLikeServiceTest {

    @Mock
    private BoardQuery boardQuery;

    @Mock
    private BoardLikeQuery boardLikeQuery;
    @Mock
    private UserCommand userCommand;

    @Mock
    private BoardCommand boardCommand;

    @InjectMocks
    private BoardLikeService boardLikeService;
  1. @MockBean : @MockBean은 스프링 컨텍스트에 mock객체를 등록하고 스프링 컨텍스트에 의해 @Autowired가 동작할 때, 등록된 mock객체를 사용
    @MockBean은 @InjectMocks로는 주입되지 않고 @Autowired를 사용해야 함
@MockBean
private OrderRepository orderRepository;
@MockBean
private Notification notification;

@Autowired // @MockBean으로 등록한 mock객체를 주입받아 의존성 해결
private OrderService orderService;
  1. @SpyBean : ApplicationContext에 spy객체를 추가

예를 들어 UserController에 대한 단위 테스트를 작성하고자 할 때, UserService를 사용하고 있다면 @Mock 어노테이션을 통해 가짜 UserService를 만들고, @InjectMocks를 통해 UserController에 이를 주입시킬 수 있다.

Junit5으로 테스트를 진행하기 위해서는 @ExtendWith(MockitoExtension.class) 를 사용해야 한다.

2-2 Stub로 결과처리

의존성이 있는 객체는 가짜 객체(Mock Object)를 주입하여 어떤 결과를 반환하는 것을 stubbing라고 한다. Mockito에서는 다음과 같은 stub 메소드를 제공한다.

  1. doReturn(): Mock 객체가 특정한 값을 반환해야 하는 경우
  2. doNothing(): Mock 객체가 아무 것도 반환하지 않는 경우(void)
  3. doThrow(): Mock 객체가 예외를 발생시키는 경우
  4. when() : 모의 객체(Mock)를 생성하고 해당 모의 객체의 특정 메서드 호출에 대한 동작을 정의하는 데 사용됩니다.
// 모의 객체의 메서드 호출에 대한 동작 정의, someMethod()내에 다른 메서드에 대해서도
when()으로 정의해주어야 한다.
when(someService.someMethod()).thenReturn("Mocked Result");

// 모의 객체를 사용하여 테스트 수행
String result = someService.someMethod();

// result는 "Mocked Result"와 같아야 함
  1. verify() : verify 구문은 Mockito 라이브러리를 사용하여 특정 메서드 호출에 대한 검증을 수행하는 데 사용됩니다.
verify(mockObject, times(expectedNumberOfInvocations)).methodName(argumentMatchers);

mockObject: 검증할 메서드 호출이 발생한 특정 목(Mock) 객체입니다.
times() : 메서드 호출이 몇 번 일어나야 하는지를 지정합니다. 예를 들어, times(1)은 한 번 호출되어야 함을 의미하고, times(2)는 두 번 호출되어야 함을 의미합니다.
methodName : 검증하려는 메서드의 이름입니다.
argumentMatchers (선택사항) : 메서드 호출의 인자에 대한 검증 조건을 지정합니다. 예를 들어, eq(expectedArgument)을 사용하여 특정 인자가 예상한 값과 일치해야 한다고 지정할 수 있습니다.

2-3 예시

@RestController
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;
    // UserController가 UserService에 의존성을 가지는 상황

    @PostMapping("/users/signUp")
    public ResponseEntity<UserResponse> signUp(@RequestBody SignUpRequest request) {
        return ResponseEntity.status(HttpStatus.CREATED)
            .body(userService.signUp(request));
    }

    @GetMapping("/users")
    public ResponseEntity<List<UserResponse>> findAll() {
        return ResponseEntity.ok(userService.findAll());
    }
}

이렇게 주입

@ExtendWith(MockitoExtension.class)
class UserControllerTest {

    @InjectMocks 
    // Mock를 여기에 주입해라 그럼 UserController에서 UserService를 찾아서 
    의존성 주입(즉, 해당 클래스안에서 정의된 mock객체를 찾아서 의존성을 해결)
    private UserController userController;

    @Mock
    private UserService userService;
    
}

3. persistent layer

  • 데이터에 대한 CRUD에 집중한 레이어로 서비스 로직이 포함되어서는 안된다

4. Bussiness layer

  • 비즈니스 로직을 구현하는 역할로 persistent layer 상호작용을 하며 트랜잭션을 보장해야한다.

참고
https://mangkyu.tistory.com/145
https://cobbybb.tistory.com/16

0개의 댓글