Android/Test

Test Double(테스트 더블)알아보기

봄석 2019. 10. 28. 14:59

Test Double(테스트 더블) 알아보기

xUnit Test Patterns의 저자인 제라드 메스자로스(Gerard Meszaros)가 만든 용어로, 

스턴트 더블(영화 촬영에서 말하는 스턴트 대역 배우)에서 아이디어를 얻어서 만든 용어입니다.

 

 

 

Test Double소개

 

제라드 메스자로스는 아래와 같이 Test Double을 소개합니다.

When we are writing a test in which we cannot (or chose not to) use a real depended-on component (DOC), we can replace it with a Test Double.

The Test Double doesn’t have to behave exactly like the real DOC; it merely has to provide the same API as the real one so that the SUT thinks it is the real one!

테스트 코드를 작성할 때 실제 DOC(depoended-on component; 의존 구성 요소)를 사용할 수 없다면, DOC 대신 테스트 더블로 대체할 수 있음.

테스트 더블은 실제 DOC와 똑같이 행동하지 않아도 되며, 똑같은 API만 제공하면 된다.

테스트 더블은 의존 구성요소를  사용할 수 없을 때

테스트 대상 코드와  상호작용  하는 객체입니다.

 

 

Test Double의 역할

  • 테스트 대상 코드를 격리합니다.
  • 테스트 속도를 개선합니다.
  • 예측 불가능한 실행 요소를 제거합니다.
  • 특수한 상황을 시뮬레이션합니다.
  • 감춰진 정보를 얻어냅니다.

 

 

 

Test Double의 종류

 

 1. Dummy

  • 가장 기본적인 테스트 더블
  • 단지 인스턴트화 된 객체가 필요하고,  해당 객체의 기능까지 필요하지 않은 경우에 사용
  •  해당 dummy객체의 메서드가 호출되었을 때 정상 동작은 보장하지 않음
  • 구현의 제외한 인터페이스 or 기본 클래스의 파생 객체
  • 객체는 전달되지만 사용되지 않는 객체

 

2. Stub

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

 

3. Fake

class StubCoupon {
    fun isAppliable(item: String): Boolean {
        return when {
            item == "후라이팬" -> true
            item == "시계" -> false
            else -> false
        }
    }
}

위 코드는 그때그때 다른 결과를 리턴하도록 하드 코딩되어있는 코드입니다.

위와 같은 코드는 단순한 Test Stub으로는 만들기 힘듭니다.

위 코드는 마치 실제 로직이 구현된  것처럼  보이는데 이렇게 만들어지는 객체를 fake객체라 합니다.

 

  • 복잡한 로직이나 객체 내부에서 필요로 하는 다른 외부 객체들의 동작을 단순화하여 구현한 객체
  • 동작의 구현을 가지고 있지만 실제 프로덕션에는 적합하지 않은 객체

 

4. Spy

테스트에서 특정 객체가 사용되었는지,

그리고 그 객체의 예상된 메서드가 정상적으로 호출됐는지를 확인해야 하는 상황이 생기는 경우 사용합니다.

 

  • Stub의 역할을 가지면서 호출된 내용에 대해 약간의 정보를 기록합니다.
  • 테스트에서 확인하기 위한 정보

 

 

5. 1  Mock

호출에 대한 기대를 명세하고 내용에 따라 동작하도록 프로그래밍된 객체입니다.

 

 

5.2 mock을 사용해야 할 때

  1. 테스트 작성을 위한 환경 구축이 어려울 때
    • 환경 구축을 위한 작업 시간이 많이 필요한 경우 Mock 객체를 사용
    • 특정 모듈을 아직 갖고 있지 않아서 테스트 환경을 구축하지 못할 경우
    • 타 부서와의 협의나 정책이 필요한 경우에도 Mock이 필요.
      • 연계 모듈이라서 다른 쪽에서 승인을 해줘야 테스트가 가능한 경우, 방화벽으로 막혀 있어서 통과가 어려운 경우 등이 이에 속함.
  2. 테스트가 특정 경우나 순간에 의존적일 때
  3. 테스트 시간이 오래 걸리는 경우

 

 

5.3 상태 기반 테스트 vs 행위 기반 테스트

Mock 객체를 이해하려면 상태 기반 테스트와 행위 기반 테스트에 대한 이해가 필요합니다.

 

  • 상태 기반 테스트(state base test)
    • 특정한 메서드를 거친 후, 객체의 상태에 대해 예상값과 비교하는 방식이 상태 기반 테스트.
      • eg. setName() 메소드를 호출했으면, getName() 메서드로 확인해보는 식
  • 행위 기반 테스트(behavior base test)
    • 올바른 로직 수행에 대한 판단의 근거로 특정한 동작의 수행 여부를 이용한다.
    • 메서드의 리턴 값이 없거나 리턴 값을 확인하는 것만으로는 예상대로 동작했음을 보증하기 어려운 경우에 사용.
    • eg. methodA에 A가 입력되면 methodB는 호출되지 않아야 정상이다. 그리고 B가 입력되면 methodB가 호출돼야 정상이다. 하지만 테스트 대상이 되는 methodA만 놓고 봤을 때 입력값에 대한 차이를 methodA만으로는 알아낼 수 없다. 즉 methodB의 호출 여부를 조사하지 않으면 methodA의 정상 동작 여부를 판단할 수 없다. 만일 methodA가 정상 동작했을 경우 methodB가 반드시 호출되는 구성이라면, 반대로 methodB의 호출 여부로 methodA의 정상 여부를 판단할 수 있다고 보는 것임. 따라서 이럴 때는 methodB의 호출 여부를 확인하는 것이 테스트 시나리오의 종료 조건이 됨.
      • 하지만 전통적인 테스트 케이스 작성 방식인 상태 기반 테스트에선 이런 상황에 대한 테스트 케이스를 작성하기가 매우 어렵거나 불편했음. 테스트 대상인 A가 상태를 갖고 있지 않기 때문이다.(void와 같은..)
      • 이때 테스트 스파이 객체를 사용하거나 자체적으로 검증 기능을 제공하는 Mock 객체를 따로 만들어서 테스트 케이스를 작성하는 것임.
      • 행위를 점검하는 걸로 테스트 케이스를 만드는 방식..
      • 따라서 행위 기반 테스트를 수행할 때는 예상하는 행위들을 미리 시나리오로 만들어놓고 해당 시나리오대로 동작이 발생했는지 여부를 확인하는 것이 핵심이 된다.

 

5.4 Test double mock vs Mock Object

  • 일반적인 테스트 더블은 상태(state를 기반으로 테스트 케이스를 작성
  • Mock 객체는 행위(behavior)를 기반으로 테스트 케이스를 작성(Mockito 등등)

Mock 객체는 행위를 검증하기 위해 사용되는 객체를 지칭합니다.

행위 기반 테스트는 좀 복잡한 시나리오가 사용되는 경우가 많고,

모양이나 작성 등 여러 측면에서 어색한 경우가 많아서 만약 상태 기반으로 테스트를 할 수 있는 상황이라면

굳이 행위 기반 테스트 케이스는 만들지 않는 것이 좋습니다.

 

ex) Mock Object framework ,, Mockito

Mockito는 Stub 작성과 verify가중심을 이루며 다음과 같은 순서로 진행합니다.

  1. CreateMock: 인터페이스에 해당하는 Mock 객체를 생성
  2. Stub: 테스트에 필요한 Mock 객체의 동작을 지정(필요시에만)
  3. Exercise: 테스트 메서드 내에서 Mock 객체를 사용
  4. Verify: 메서드가 예상대로 호출 됬는지 검증