본문 바로가기

Programing/OpenSource

[Spock Framework] Mock vs Stub

Spock Framework Reference Documentation 을 보면 다른 종류의 Mock Objects로 Stub을 소개하고 있다.

레퍼런스에서는 mock 은 stubbing과 mocking을 둘 다 할 수 있고 ,stub은 단지 stubbing 만 할 수 있다고 나와 있다.

가장 큰 차이는 stub은 몇 번 호출되었는지를 물어볼 수 없는 차이가 있다.

 

하지만 이것으로는 Mock() 와 Stub()을 언제 써야할 지 명확하지 않다.

우연히 처음에는 Stub()을 사용하다가 카운팅 여부를 확인해야 해서 이후에 Mock()으로 바꾸는 작업이 있었는데

이 side-effect로 다른 테스트 케이스가 깨지는 경험을 하게 되어 차이를 이제야 이해할 수 있었다.

 

예를 들어 아래와 같이 CancelService가 OrderService와 InventoryService를 내부에서 사용하고 있다.

이것을 테스트 코드로 skeleton을 구성할 때 아래와 같이 구성이 가능하다.

import spock.lang.Specification
import spock.lang.Subject

class CancelServiceSpec extends Specification {
    @Subject
    private CancelService sut
    private OrderService orderService
    private InventoryService inventoryService

    def setup() {
        orderService = Stub()
        inventoryService = Stub()
        sut = new CancelService(orderService, inventoryService)
    }

만약 테스트 관심사가 OrderService 일 경우 InventoryService 의 경우 Stub()으로 되어 있기 때문에 

CancelService 내부에서 InventoryService의 메서드가 호출이 되어도 적절한 객체가 반환이 된다.

 

만약 아래와 같이 Mock() 으로 바뀌게 되면 stubbing을 해주지 않았을 경우 null 이 반환된다.

import spock.lang.Specification
import spock.lang.Subject

class CancelServiceSpec extends Specification {
    @Subject
    private CancelService sut
    private OrderService orderService
    private InventoryService inventoryService

    def setup() {
        orderService = Stub()
        inventoryService = Mock()	// mock!
        sut = new CancelService(orderService, inventoryService)
    }

따라서 경우에 따라서 NupNullPointerException 가 발생하면서 Stub()일 때는 문제 없던 다른 테스트 코드에서 예외가 발생하면서 테스트가 깨질 수 있다.

단지 XXXApi가 Stub()에서 Mock()으로 바뀌었을 뿐인데...