ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • MockK 에 대해서 알아보자(기초편)
    Tool 2023. 4. 10. 19:41

    개요

    MockK 에 대한 설명만을 진행하기에 테스트와 관련된 부분은 설명이 없는점 양해 부탁드립니다.
    다만, Junit 기반으로 쉽게 확인할 수 있도록 코드를 정리해놓았으니
    Junit 에 대한 내용이 필요하시다면 아래 링크를 통해 확인하면 좋을 것 같습니다.


    Mocking이 필요한 이유

    애플리케이션이 데이터베이스를 사용, 외부 API 호출, 자사 플랫폼의 API 를 호출할만큼 복잡해졌다 가정한다.
    애플리케이션의 기능들을 검증하기 위해 외부 API 호출과 같은 작업들도 테스트해야 한다.

    하지만, 테스트를 진행함에 있어 매번 외부 API 호출과 같은 작업을 하는 것은 매우 복잡하고 번거로운 일이다.
    특히 이러한 서비스를 테스팅하기 위해서는 실제 서비스와 동일한 수준의 코드를 작성해야하며
    또한, 이러한 서비스 클래스들이 미리 정의되어 있지 않는다면 테스트를 진행할 수도 없다.
    결국, 테스트라는 본질을 잃어버리고 하나의 서비스를 만드는 작업이 되어버린다.

    Mock은 테스트에 필요한 서비스를 호출한다고 가정을 한 모형 객체이다.
    Mock 객체를 이용해서 우리는 실제 서비스를 이용하는 것처럼 기능을 테스트 할 수 있다.
    Mock 객체를 이용해서 구현되어 있지 않은 서비스를 예상하고 기능을 테스트 할 수 있다.

    그렇기 때문에, Mock은 단순히 모형 객체가 아닌 테스트라는 본연에 작업에 집중할 수 있게 해주는 객체이다.

    Mock이 필요한 상황을 정리해보면 아래와 같다.

    • 테스트 환경을 구축하는데 시간이 오래걸리는 경우
    • 특정 모듈을 갖고 있지 않아 테스트 환경을 구축하기 어려운 경우
    • API 호출 및 사용에 대한 협의나 정책이 필요한 경우
    • 테스트가 특정 경우나 순간에 의존적인 경우
    • 테스트 시간이 오래 걸리는 경우
    • 개인 PC의 성능이나 서버의 성능문제로 테스트가 오래 걸릴수 있는 경우

    MockK 모의 객체 생성 문법

    Mock

    메서드의 응답을 설정할 수 있다는 점에서 스텁과 비슷한 역할을 한다. Mock 은 모든 상호작용을 저장해서 나중에 단언문에 활용할 수 있도록 해준다. (예를 들면 메서드 호출이 딱 1번만 일어난다던가, 특정 메서드는 호출하지 말아야 한다던가)

    • mock<T> 방식 : 가장 일반적인 방식으로 모킹할 제네릭으로 클래스를 감싼다.
    • 타입 추론 방식 : 타입 추론을 이용한 방식으로 모킹한 클래스를 타입으로 명시한다.
    • 어노테이션 방식 : 늦은 의존성 주입을 통한 방식으로 @MockK 어노테이션과 lateinit var 을 정의한다.
      • 메서드 파람 방식 : 어노테이션을 활용하여, 모킹 클래스를 생성하면서 파라미터로 전달할 수 있다.

     

    Relaxed Mock

    호출하는 외부 시스템이 많을 수록, 이에 따른 mocking 값을 정의해주는 것이 불편할 수 있다.
    또는 결과 값을 굳이 중요하지 않았을 때에도 이렇나 mocking 값을 정의해주는 것이 불편할 수 있다.
    (예를 들면, 알림 or 푸시 서버에 데이터를 전송 등이 있다)

    이럴 때 사용하는 mocking 방식으로 Relaxed Mock 이 있다.

    • mockK 를 이용해 생성하되 relaxed = true 파라미터를 추가하면된다.
    • 별도의 relaxed 타입의 어노테이션을 제공해준다.

     

    • 메서드에 대한 별다른 mocking 값을 정의해주지 않아도 에러 없이 테스트가 성공한다.

     

    Spy

    Spy 테스트는, 의존성을 감시하는 역할로 실제 객체를 감싸서 그 행동을 관찰한다.
    엄밀히 말해 객체를 시뮬레이션 하는게 아니라, 감시하고 있는 근본 객체와의 모든 상호작용을 기록한다.

    그러다보니, Spy 는 Mock 과 달리 실제 모킹한 부분을 제외하고서는 실제 객체의 동작이 그대로 수행된다.
    테스트 대상 메서드가 실제 의존 대상과 어떻게 상호작용하는지 단언하고자 하는 경우에 사용된다.

    단, 실제 객체를 감싸서 특정 메서드만 호출하는 것이다보니, 실제 객체 생성에 대한 개발 비용이 추가된다.


    MockK 모의 객체 공통 문법

    Answer 정의

    • returns : 결과값을 반환한다.
    • returns A antThen B : 결과값들을 반환한다.
    • returnsMany : 결과값들을 반환한다.
    • throws : 예외를 반환한다.
    • just Runs : 결과값을 반환하지 않는다.
    • answers : 람다를 이용한 로직이 필요한 결과값을 반환한다.

     

    Argument 정의

    • general : 가장 일반적인 방법으로 파라미터에 매개변수를 직접 명시한다.
    • any : 명시된 값 대신, 아무 값을 허용할 때 사용한다.
    • varargs : varags 표현에 맞추어, 동적 파라미터를 지원할 때 사용한다.
      • 람다 표현식을 지원해주어서, Argument 에 대한 특정 조건문을 넣을 수 있다.
      • *varargAll : 모든 인수가 해당 조건에 맞아야 한다.
      • *varargAny : 일부 인수가 해당 조건에 맞아야 한다.

     

    호출 여부 검증

    • verify
      • no args : 단순히 호출했는지를 검증
        • wasNot Called : 어느 메서드도 호출 안했는지 검증
      • atLeast = n : 최소 n회 호출했는지 검증
      • atMost = n : 최대 n회 호출했는지 검증
      • exatly = n : 정확히 n회 호출했는지 검증

     

    • verifyAll : 특정 순서에 얽매이지 않고 호출했는가 자체를 검증
    • verifySequence : 순서대로 호출했는가를 검증
    • verifyOrder : 앞선 순서로 호출했는가를 검증

     

    Capturing

    capturing은 mock 또는 spy 객체에 대해서 함수에 들어가는 파라미터의 값을 가져와서 검증하기 위해서 사용하는 방법
    mockk에서는 every 또는 verify 에 caputre()를 통해서 slot에 값을 저장할 수 있고 이를 slot.captured을 통해서 가져올 수 있다.

     

    Partial Mocking

    mocking 한 객체를 이용하지만, 다른 함수 또는 특정 인수에 대해서 실제 메서드를 호출해야할 때 사용한다.
    answers { callOriginal() } 를 활용하면 된다.


    Spring With MockK

    의존성

    testImplementation("com.ninja-squad:springmockk:버전")
    

     

    사용법

    @MockkBean 
    private lateinit var userRepositoryV1: UserRepository
    
    @SpykBean 
    private lateinit var userRepositoryV2: UserRepository
    
    
    • @MockkBean : 스프링으로 등록된 Bean 을 Mock 객체로 만든 인스턴스
    • @SpykBean : 스프링으로 등록된 Bean 을 Spy 객체로 만든 인스턴스

     

    참고하면 좋을 자료들

     

    'Tool' 카테고리의 다른 글

    Liquibase 를 이용한 DB Schema 형상 맞추기  (0) 2023.03.02
Designed by Tistory.