Home Mock 그리고 Mockito, BDDMockito
Post
Cancel

Mock 그리고 Mockito, BDDMockito

단위 테스트를 하기 위해서는 한 번에 메서드 하나만을 실행해보아야 한다. 그런데 이러한 메서드가 네트워크, DB 등 제어하기 어려운 것들에 의존하고 있다면 어떻게 단위테스트를 해야할까?

이러한 부분을 돕기위해 Mock이라는 것이 등장했다.

Mock

실제 객체를 만들어 사용하기에 시간, 비용 등의 Cost가 높거나 혹은 객체 서로간의 의존성이 강해 구현하기 힘들 경우 가짜 객체를 만들어 사용하는 방법을 뜻한다.

언제 필요한가?

  • 테스트 작성을 위한 환경 구축이 어려운 경우
  • 테스트가 특정 경우나 순간에 의존적인 경우
  • 테스트 시간이 오래 걸리는 경우
  • 개인 PC의 성능이나 서버의 성능 문제로 오래 걸릴 수 있는 경우 시간을 단축하기 위한 용도로 사용하기도 한다.

분류

Mock에 대한 기본적인 분류 개념을 알아본다.

테스트 더블

  • 테스트를 진행하기 어려운 경우 이를 대신해 테스트를 진행할 수 있도록 만들어주는 객체를 뜻한다.
  • Mock 객체와 유사한 의미를 가지며 테스트 더블이 조금 더 상위 의미로 사용된다.

더미 객체(Dummy Object)

  • 단순히 인스턴스화 될 수 있는 수준으로만 객체를 구현한다.
  • 인스턴스화된 객체가 필요할 뿐 해당 객체의 기능까지는 필요하지 않은 경우에 사용한다.

테스트 스텁(Test Stub)

  • 더미 객체보다 조금 더 구현된 객체로 더미 객체가 마치 실제로 동작하는 것처럼 보이게 만들어 놓은 객체이다.
  • 객체의 특정 상태를 가정해 만들어 특정 값을 리턴해주거나 특정 메시지를 출력해주는 작업을 한다.
  • 특정 상태를 가정해 하드코딩된 형태이기 때문에 로직에 따른 값의 변경은 테스트할 수 없다.
  • 어떤 행위가 호출됐을 때 특정 값으로 리턴시켜주는 형태가 Stub이다.

페이크 객체(Fake Object)

  • 여러 상태를 대표할 수 있도록 구현된 객체로 실제 로직이 구현된 것 처럼 보이게 한다.
  • 실제로 DB에 접속해 비교할 때와 동일한 모양이 보이도록 객체 내부에 구현할 수 있다.
    • 테스트 케이스 작성 시 다른 객체들과의 의존성을 제거하기 위해 사용한다.
    • 페이크 객체를 만들 때 복잡도로 인해 노력이 많이 들어갈 경우 적절한 수준에서 구현하거나 Mock 프레임워크를 사용한다.
    • 페이크 객체를 생성하기 위한 노력이 많이 필요한 경우 실제 객체를 가져와 테스트한다.

테스트 스파이(Test Spy)

  • 테스트에 사용되는 객체, 메서드의 사용 여부 및 정상 호출 여부를 기록하고 요청 시 알려준다.
  • 테스트 더블로 구현된 객체에 자기 자신이 호출되었을 때 확인이 필요한 부분들을 기록하도록 구현한다.
  • 특정 테스트 메서드가 몇 번 호출 되었는 지 필요한 경우 전역변수로 카운트를 설정하고 특정 테스트 메서드에 카운트를 올리는 부분을 추가해 이 카운트를 가져오는 메서드를 추가한다.
  • 특정 메서드가 호출되었을 때 또 다른 메서드가 실행되어야 한다와 같은 행위 기반 테스트가 필요한 경우 사용된다.

Mock 객체 (Mock Object)

  • 행위를 검증하기 위해 사용되는 객체를 지칭하며 수동으로 만들 수도 있고 프레임워크를 통해 만들 수 있다.
  • 행위 기반 테스트는 복잡도나 정확성 등 작성하기 어려운 부분이 많기 때문에 상태 기반 테스트가 가능하다면 행위 기반 테스트는 만들지 않는다.
  • Mock 객체는 테스트 더블 하위객체로써의 좁은 의미와 테스트 더블을 포함한 넓은 의미 2가지로 사용될 수 있다.

Mock Object는 행위 검증(behavior verification)에 사용하고, Stub은 상태 검증(state verification)에 사용하는 것이다.

Mockito

Mockito는 Java 오픈 소스 테스트 프레임워크이다. Mockito를 사용해 실제 객체를 모방한 가짜 객체, Mock 객체 생성이 가능해진다.

메서드 종류

  • mock()
    • Mock 객체를 생성해주는 메서드.
1
Person p = Mockito.mock(Person.class);
  • when()
    • Mock 객체의 메서드 반환 값을 지정해주는 메서드
1
2
Mockito.when(p.getName()).thenReturn("oliver");
//p.getName()이 호출되었을 때 "oliver"를 반환하도록 지정.
  • verify()
    • Mock 객체에서 메서드가 호출되었는지를 확인해주는 메서드
1
2
Mockito.verify(p).getName();
//p.getName()이 호출되었는 지 확인한다.
  • times()
    • 지정한 횟수 만큼 Mock 객체의 메서드가 호출되었는 지 확인해주는 메서드
1
2
Mockito.verify(p, times(1)).getName();
//p.getName()이 1번 호출되었는 지 확인한다.
  • never()
    • Mock 객체의 메서드가 호출이 되지 않았는지 확인해주는 메서드
1
2
Mockito.verify(p, never()).getName();
//p.getName()이 호출이 되지 않았는지 확인한다.
  • atLeast(), atMost()
    • Mock 객체의 메서드가 최소, 최대 지정 횟수만큼 호출 되었는 지 확인해주는 메서드
  • timeOut()
    • Mock 객체의 메서드가 지정된 시간 안에 호출되었는 지 확인해주는 메서드

임의의 값을 넣어줄 때 사용하는 메서드들도 있다.

  • anyString(), anyObject()
1
Mockito.verify(p).setName(Mockito.anyString()));

BDDMockito

Mockito를 상속한 클래스이다. 동작이나 사용법에는 별 다른 차이가 없다.

BDD는 Behavior-Driven Development의 약자이다. 행위 주도 개발을 뜻한다.

테스트 대상의 상태 변화를 테스트하는 것이고, 시나리오를 기반으로 테스트하는 패턴을 권장한다. 이러한 패턴의 기본은 보통 Given - When - Then 구조를 가진다.

이는 테스트 대상이 A상태에서 출발하며(Given), 어떤 상태 변화를 가했을 때(When), 기대하는 상태로 완료되어야 한다(Then).

Mockito를 사용하면 given이 위치할 코드에 Mockito.when() 메서드가 사용된다. BDDMockito는 BDD를 사용하여 테스트 코드를 작성할 때 시나리오에 맞게 테스트 코드가 읽힐 수 있도록 이름을 변경한 프레임워크라고 봐도 된다.

  • Mokito.when() -> BDDMockito.given()
  • Mockito.thenReturn() -> BDDMockito.willReturn()
  • Mockito.verify() -> BDDMockito.then().should()

이러한 네이밍의 변경이 많다.

This post is licensed under CC BY 4.0 by the author.