Mockito

이한수·2022년 7월 27일
0

TEST

목록 보기
3/3
post-thumbnail

Mock이란?

  • 진짜 객체외 비슷하게 동작하지만 , 프로그래머가 직접 행동을 관리하는 객체이다.

Mockito란?

  • Mock 객체를 생성하고 관리하고 검증할 수 있는 방법을 제공.

-단위 테스트를 위한 mocking 프레임워크 라고 보면 된다.

예시

public class User {

    public User(){}

    public User(String username , Integer age){
        this.username = username;
        this.age = age;
    }

    private Integer id;
    private String username;
    private Integer age;
    
@Service
public class UserService {

   private UserRepository userRepository;

   public UserService(UserRepository userRepository){
       this.userRepository = userRepository;
   }


   public User save(User user){
       return userRepository.save(user);
   }

   public User findUser(Integer id){
       return userRepository.findUser(id)
               .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 회원"));

   }

   public List<User> findUserList(){
       return userRepository.findUserList();
   }

위와 같이 User 엔티티가 있고, UserService를 테스트한다고 가정해보자.

  • UserService는 UserRepository를 내부에서 참조하고 있다.
@ExtendWith(MockitoExtension.class)
class UserServiceTest{

	@Mock
    UserRepository userRepository;
		
    @before
    void createUserService(UserRepository userRepository){
    	UserService userService = new UserService(userRepository);
    }
}
  • Mockito를 사용하기 위해서, 먼저 @ExtendWith(MockitoExtension.class)로 확장한다.

  • @Mock 어노테이션을 이용하여 생성한 UserRepository Mock객체를 이용하여 UserService를 생성한다.

더 간편하게 하기 원한다면,


@before
    void createUserService(@Mock UserRepository userRepository){
    	UserService userService = new UserService(userRepository);
    }

더 간단하게,,,??

	@Mock
    UserRepository userRepository;
    
    @InjectMocks
    UserService userService;

이렇게도 사용할 수 있다.

Mock 객체 의존성 주입

어떻게 생성하는지는 알겠으니 , Mock객체 생성 방법 관련하여 몇가지 어노테이션을 살펴보자.

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

  • @Mock : Mock 객체를 생성하여 준다.

  • @Spy : Stub하지 않은 메서드들은 원본 메서드 그대로 사용하는 어노테이션

  • @InjectMocks : @Mock 또는 @Spy로 생성된 가짜 객체를 주입시켜주는 어노테이션.

@Spy는 무엇인가하는 생각이 든다.
먼저, Mock객체가 하는 행동을 설명한 뒤에 Stub가 무엇인지 설명한 뒤에 알아보자.

Mock객체의 행동

  • Null을 리턴한다.

  • Primitive 타입은 기본 Primitive값을 리턴한다.

  • 콜렉션은 비어있는 콜렉션

  • Void메소드는 예외를 던지지 않고, 아무런 일도 발생하지 않는다.

Null을 리턴하는 경우와 void시 예외를 던지지 않는 것만 살펴보자.

	@Mock
    UserRepository userRepository;

    @InjectMocks
    UserService userService;

	//null을 리턴하는 경우
    @Test
    void saveTest(){
        User user = new User("홍길동",20);
        User findUser = userService.save(user);

       
        assertThat(findUser).isNull();
    }
    
    
    //void메소드가 예외를 던지지 않을 경우
    @Test
    void deleteTest(){
    	int id = 100;
        userService.deleteUser(100);
    }
    

null일 경우.

  • userService의 save() -> userRepository의 save()를 호출
  • userRepository는 저장한 뒤에 저장한 객체를 반환한다. service 또한 마찬가지.

하지만 실제로는 null이 반환되고, 테스트는 통과 된다.

Optional의 경우 Optional.empty가 나온다.

예외를 던지지 않을 경우.

  • userRepository에는 id값이 존재하지 않는 회원 조회시 , Exception을 던지게 하였다.

  • 하지만 , 아무런 일도 발생하지 않는다.

이러한 결과들이 발생되기 때문에 , Mock객체가 호출되었을때 , 행위들을 직접 지정해줄 수 있는데 이것을
"stubbing" 이라고한다.

	@Mock
    UserRepository userRepository;

    @InjectMocks
    UserService userService;

	//null을 리턴하는 경우
    @Test
    void saveTest(){
        User user = new User("홍길동",20)
        
       doReturn(user).when(userRepository).save(user);
        
        User findUser = userService.save(user);

       
        assertThat(findUser).isNotNull();
    }
    
    
    //void메소드가 예외를 던지지 않을 경우
   @Test
    void deleteTest(){
        int id = 100;

        doThrow(new IllegalArgumentException("존재하지 않는 회원")).when(userRepository).deleteUser(id);

        assertThrows(IllegalArgumentException.class , () -> userService.deleteUser(id));
    }
    

위와 같이 mock객체가 호출되었을 때, 어떤 행위를 할것인지 지정해줄 수 있다.

다시 돌아가 @Spy 를 본다면 이해될 것이다.

주의 호출될 때 넘겨주는 인자가 달라질 경우 ,stubbing이 적용되지 않는다.
어떤 인자를 넘기더라도 호출되게끔 하고 싶다면 any() 를 사용하자.

Stubbing 메소드

  • doReturn() : Mock 객체가 특정 값을 반환해야 하는 경우.

  • doNothing() : Mock 객체가 아무것도 반환하지 않는 경우.

  • doThrow() : Mock 객체가 예외를 발생시키는 경우.

확인하기

만일, userRepository의 save()라는 메소드가 진짜 호출 되었는지 확인하려면, 아래처럼 쓸 수 있다.

verify(userRepository , times(1)).save(user);
  • save()가 1번 호출 되었는지 확인 한다.
verify(userRepository , times(1)).save(any());
  • any()라는 키워드를 사용하여 어떤 인자를 넘기더라도 호출되었는지 확인할 수 있다.
verify(userRepository , times(1)).save(user1);
verify(userRepository , times(1)).save(user2);

InOrder inOrder = inOrder(memberRepository);

inOrder.verify(memberRepository).save(user1);
inOrder.verify(memberRepository).save(user2);
  • InOrder를 이용하여 위처럼 순서를 확인할 수 있다.

이 밖에도 많지만 이정도만 적음.

profile
성실하게

0개의 댓글