이번에는 심화편이다.
이 글을 읽기 전에 기본편을 먼저 보고 오면 좋다.

Gradle

  • Mockito는 기본적으로 Java 기반의 라이브러리이다.
  • 그렇기 때문에 Kotlin을 함께 사용하면 모호한 부분이 존재하기 마련이다.
  • 예를 들면 다음과 같은 구문들이 있다.
1
2
3
4
5
6
7
8
9
public class PersonTest{
@Test
public void mockTest(){
Person p = mock(Person.class);
assertTrue(p!=null);

when(p.getName()).thenReturn("VictoryWoo")
}
}
  • 여기서 when문을 보면 그냥 쓰였다.
  • 이는 자바이기 때문에 그대로 쓰이지만 코틀린에서는 다음과 같이 쓰인다.
1
2
3
4
5
6
7
8
9
10
class PersonTest{
@Test
fun personMockTest(){
val p: Person = mock(Person::class.java)
assertTrue(p != null)


`when`(p.name).thenReturn("VictoryWoo")
}
}
  • 표현이 다르다. when의 앞뒤로 `가 붙는다.
  • 자바와 코틀린을 사용할 때, 구문이 맞지 않아서 생기는 문제이다. 크게 신경 쓰이지 않을 수도 있지만, 필자는 신경이 쓰인다.
  • 이뿐만 아니라, 앞서 말했던 Kotlin에서는 모든 클래스가 기본적으로 final로 선언되어 있어 Mockito는 기본적으로 Mocking을 할 수 없는 등의 여러 문제가 있다.
  • 그래서 Niek Haarman이라는 분이 유지 및 관리하는 mockito-kotlin 라이브러리를 사용하면 깔끔하게 해결할 수 있다.

Mockito-Kotlin

app/build.gradle 파일에 아래와 같이 추가하면 된다.

1
2
testImplementation 'org.mockito:mockito-inline:2.21.0'
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
  • 첫 번째는 Mockito-Kotlin 의존성이다.
  • 두 번째는 Kotlin의 클래스는 기본이 final이기 때문에 mockito-inline을 추가해주어 open 클래스로 매번 선언해야 하는 번거로움을 제거한다.

Mock 객체 만들기

  • mockito-kotlin에서는 mock을 생성할 때, 더이상 클래스를 파라미터로 전달할 필요가 없다. 코틀린의 특성처럼 타입을 유추할 수 있다면 생략이 가능하다.
  • 또한, p2처럼 타입을 추론할 수 없다면 아래와 같이 <> 안에 타입을 명시해준다.
1
2
val p: Person = mock()
val p2 = mock<Person>()

# Stubbing

  • Stub : 토막, 꽁초, 남은 부분, 몽당연필이라는 뜻으로 dummy 객체가 마치 실제로 동작하는 것처럼 보이도록 만들어놓은 것을 말한다.

위키피디아에서는 test stub을 아래와 같이 정의한다.

Test stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what’s programmed in for the test.

  • 스텁은 "canned answer"를 호출한 쪽에 제공한다는 것이다.
  • canned answer : 미리 준비된 답변은 일반적인 질문에 대한 미리 정해진 답변이라는 뜻이다. 즉, stub은 실제 코드나 아직 준비되지 못한 코드를 미리 정해진 답변으로 가장하는 매커니즘이다.

# Stub 특징

  • dummy 객체가 실제로 동작하는 것처럼 보이게 만들어 놓은 객체
  • 실제 코드나 아직 준비되지 못한 코드의 행동을 가장하는 행위
  • 호출자를 실제 구현물로부터 격리시키는 목적으로 사용가능
  • 인터페이스 or 기본클래스가 최소한으로 구현된 상태
  • 테스트에서 호출된 요청에 대해 미리 준비해둔 결과를 제공한다.

# Stub이 주로 사용되는 경우

  • 구현이 되지 않은 함수나 라이브러리에서 제공하는 함수를 사용하고자 할때
  • 함수가 반환하는 값을 임의로 생성하고 싶을때
  • 복잡한 논리 흐름을 가지는 경우, 테스트를 단순화하고 싶을때
  • 의존성을 가지는 유닛의 응답을 모사하여 독립적인 시험 수행을 하고자할 때

# Test Stub을 사용하여 얻을 수 있는 이점

  • 의존하는 것에 대하여 독립적으로 개발/테스트가 가능하다.
    • Interface만 존재하는 것을 Stub으로 개발하고 테스트할 수 있다.
  • 촘촘한 테스트가 가능하다.
    • Stub으로 다양한 응답결과 케이스를 만들어 테스트할 수 있다.
  • mockito-kotlin은 when을 쓰지 않고 whenever를 사용한다.
1
2
val p : Person = mock()
whenever(p.name).thenReturn("VIC")
  • 위의 식처럼 stubbing을 하거나 혹은 람다식을 사용할 수도 있다.
1
2
3
4
5
val p: Person = mock {
on { name } doReturn "LEE"
}

assertTrue("LEE" == p.name) // 테스트 통과.

Mockito 다양한 함수

Mockito에서 자주 사용되거나 유용한 함수들에 대해서 알아볼 것이다.

더 자세한 내용은 돼지왕 왕돼지 놀이터 블로그 글을 참고하면 될 것 같다.

참고