Mockito와 통합 테스트

Song Chae Won·2023년 10월 10일
0

EFUB_정리

목록 보기
4/4
post-thumbnail

Mock

Mock 소개

Mock 객체

진짜 객체와 비슷하게 동작하지만 프로그래머가 직접 그 객체의 행동을 관리하는 객체

  • 테스트 작성을 위한 환경 구축이 어려울 때, 테스트하고자 하는 코드와 엮인 객체들을 대신한다.
  • 행위를 기반으로 테스트 케이스를 작성한다.

왜 사용할까?

실제 객체를 만들기엔 비용과 시간이 많이 들거나 의존성이 길게 걸쳐져 있어 제대로 구현하기 어려울 경우,
실제 사용하는 모듈을 사용하지 않고 실제의 모듈을 흉내내는 가짜 모듈을 작성하여 테스트의 효용성을 높이기 위해 사용한다.

언제 사용할까?

ex) 데이터베이스를 사용하거나, 외부 API 호출할 때
매번 실제 데이터베이스나 API를 사용하여 테스트할 수 없다.
➡️ Mock을 만들어서 테스트 한다.

테스트 작성을 위한 환경 구축이 어려운 경우
• 환경 구축을 위한 작업 시간이 많이 필요할 때 (DB, 웹서버, FTP서버 등)
• 특정 모듈을 갖고 있지 않아서 테스트 환경을 구축하지 못할 때
• 타 부서와의 협의나 정책이 필요할 때

  • 테스트가 특정 경우나 순간에 의존적인 경우
  • 테스트 시간이 오래 걸리는 경우
  • 개인 PC의 성능이나 서버의 성능 문제로 오래 걸릴 수 있는 경우 시간을 단축하기 위해

Mock 분류

테스트 더블 Test double

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

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

(2) 테스트 스텁
• 더미 객체보다 좀 더 구현된 객체로 더미 객체가 마치 실제로 동작하는 것처럼 보이게 만들어 놓은 객체
• 객체의 특정 상태를 가정해서 만들어 특정 값을 리턴해 주거나 특정 메시지를 출력

(3) 페이크 객체
• 여러 상태를 대표할 수 있도록 구현된 객체로 실제 로직이 구현된 것처럼 보이게 하는 객체
• 테스트케이스 작성을 위해서 다른 객체들과의 의존성을 제거하기 위해 사용

(4) 테스트 스파이
• 테스트에 사용되는 객체, 메소드의 사용 여부 및 정상 호출 여부를 기록하고 요청 시 제공
• 아주 특수한 경우를 제외하고는 잘 쓰이지 않으며 보통 Mock 프레임워크에서 기본적으로 기능을 제공
하므로 Mock을 사용

(5) Mock 객체
• 행위를 검증하기 위해 사용되는 객체를 지칭
• 수동으로 만들 수도 있고 프레임워크를 통해 만들 수 있음
• Mock 객체는 테스트 더블의 하위 객체로서 좁은 의미와 테스트 더블을 포함한 넓은 의미 2가지로 사용

MockMvc

  • 스프링에서 MVC 테스트를 하기 위한 클래스

주입 방법
1. @SpringBootTest + @AutoConfigureMockMvc
: 통합 테스트 할 때

  1. @WebMvcTest
    : MVC만 테스트 할 때

perform()
• 요청을 전송하는 역할
• 결과로 ResultActions 객체를 반환한다.
• ResultActions 객체는 리턴 값을 검증하고 확인할 수 있는 andExcpect() 메소드를 제공한다.

Mockito

Mock 객체를 쉽게 만들고, 관리하고, 검증할 수 있는 방법을 제공하는 프레임워크

• 간편한 사용법으로 빠르게 확산되고 있으며 상태 기반 테스트를 지원한다.
• Mock의 행동을 정하는 stubbing, 정상적으로 작동하는지에 대한 verify 등 다양한 기능을 제공해준다. (여기서 stubbing은 mock 객체의 행동을 조작하는 것)
• 가짜 객체에 원하는 결과를 stub하여 단위 테스트를 진행할 수 있다.

장점
• 사용법이 단순하다.
• 리팩토링이 쉽다.
• API가 간단하다.
• Mock 만드는 방법을 단일화했다.
• 테스트 stub을 만들기 쉽다.
• 테스트 stub을 만드는 것과 검증을 분리한다.
• 실패 시에 발생하는 스택 트레이스가 깔끔하다.
• 작성이 어렵지 않아 테스트 자체에 집중할 수 있다.

Mockito 사용법

  1. CreateMock : 인터페이스에 해당되는 Mock 객체를 만든다.
  2. Stub : 테스트에 필요한 Mock 객체의 동작을 지정한다.
  3. Excercise : 테스트 메소드 내에서 Mock 객체를 사용한다.
  4. Verify : 메서드가 예상대로 호출되었는지 검증한다.

1. Mock 객체 만들기

Mock 객체는 언제 만들까?
1. Service에서 인터페이스를 참조하는데, 로직을 구성할 때 해당 인터페이스에 있는 메소드들을 가져와 로직을 구성하고자 한다.
2. 그러나 인터페이스의 구현체는 따로 구현되어 있지 않다.
3. 이럴 때, 인터페이스가 구현되어 있다는 가정하에 구성한 Service 로직을 실행하고자 할 때 Mock 객체를 만들어 테스트 한다.
4. 이 때, Mock 객체는 참조하는 인터페이스가 되는 것이다.

2. Stubbing

Mock 객체의 행동
• Null을 리턴한다. (Optional 타입은 Optional.empty 리턴)
• Primitive 타입은 기본 Primitive 값을 따른다.
• Collection은 비어있다.
• void 메소드는 예외를 던지지 않고 아무 일도 발생하지 않는다.
➡️ 이런 특성을 가진 Mock 객체를 우리가 원하는 것을 리턴하도록 조작하는 것 = Stubbing

1) 특정한 매개변수를 받은 경우 특정한 값을 리턴하도록 설정
• when(), thenReturn() 메서드 활용

2) 특정한 매개변수를 받은 경우 특정한 예외를 던지도록 설정
• when(), thenThrow() 메서드 활용

3) void 메소드가 특정 매개변수를 받거나 호출된 경우 예외를 발생시키도록 설정
• doThrow(), when() 메서드 활용

4) 메소드가 동일한 매개변수로 여러 번 호출될 때 각기 다르게 행동하도록 설정
• ex) when(), thenReturn(), thenThrow() 메서드 활용

3. Verifying

1) 메소드가 몇 번 호출 됐는지 검증
• verify([mock 객체], [호출 돼야 하는 횟수]).[mock 객체 메소드]
• ex) verify(memberService, times(1)).notify(study);

2) 메소드 호출 순서 검증
• inOrder.verify([mock객체]).[메소드명]
• ex) inOrder.verify(memberService).notify(study);
inOrder.verify(memberService).notify(member);

3) 더 이상 검증할 호출 메소드가 없는지 체크
• verifyNoMoreInteractions([mock객체])
• ex) verifyNoMoreInteractions(memberService);

Mockito BDD 스타일 API

BDD

• Behavior Driven Development: 행위 주도 개발
• 애플리케이션이 어떻게 행동해야 하는지에 대한 공통된 이해를 구성하는 방법으로, TDD에서 창안했다.

TDD와 BDD의 차이

• BDD와 TDD는 서로 상호보완적인 관계에 있다.
• BDD로 어떠한 행위를 테스트하고 해당 행위에서 깊게 테스트 하기 위해서 TDD가 필요하다.
• TDD는 테스트 코드를 모듈 단위로 작성하므로 개발자가 읽는 코드이다.
➡️ 개발자에게 친화적인 언어로 작성한다.
• BDD는 테스트 코드를 시나리오 단위로 작성하므로 개발자뿐만 아니라 기획자, 테스터와 같은 비개발자도 읽을 수 있다.
➡️ 누구나 읽을 수 있는 보편 언어로 작성한다.

행동에 대한 스펙

• Title: 행동의 이름
• Narrative: 행동에 대한 설명
• As a (역할) / I want (기능) / so that (이점, 기능을 원하는 이유)
• Acceptance criteria
• Given (어떠한 상황이 주어졌을 때) / When (어떤 것을 하면) / Then (어떻게 될 것이다)

profile
@chhaewxn

0개의 댓글