網頁

2018/8/15

Mockito Mock與Spy的差別

在Mockito中有Mock及Spy物件,分別可以用@Mock@Spy來注入,但兩者差異如下。

Mock物件從頭到尾都是假的物件,當呼叫mock的方法時其實並不會做任何事,如果方法有返回值,呼叫後僅回傳null。

Spy物件幾乎同等於真的物件,當呼叫spy的方法時會呼叫真正的原方法,也會返回原方法執行後應返回的值。

觀察下面範例就可以看出Mock與Spy的區別了。

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class MockitoTest {

  @Mock
  private List mockList;

  @Spy
  private List spyList = new ArrayList();

  @Test
  public void testMockList() {
    // mock物件是假的, 所以呼叫方法時其實並不會做任何事
    mockList.add("test"); // 並沒有真的把"test"字串加入mockList,
    assertNull(mockList.get(0)); // 如果原方法有返回值, 則mock物件的方法僅返回null
  }

  @Test
  public void testSpyList() {
    // spy物件會呼叫真正的原方法
    spyList.add("test"); // 真的把"test"加入spyList
    assertEquals("test", spyList.get(0)); // spy物件的方法會返回值
  }

  @Test
  public void testMockWithStub() {

    String expected = "Mock 100";

    // stub mock物件的get(100)方法
    when(mockList.get(100)).thenReturn(expected);

    assertEquals(expected, mockList.get(100));
  }

  @Test
  public void testSpyWithStub() {

    String expected = "Spy 100";
    // stub spy物件的get(100)方法
    // spy物件使用doReturn()來stub方法的返回值
    doReturn(expected).when(spyList).get(100);

    assertEquals(expected, spyList.get(100));
  }

}

此時可能又會想,那spy物件和真的物件有什麼差別? 幹嘛不用真的物件就好了。因為真的物件並無法stub其方法,也無法追蹤其行為,所以要改用Spy物件來達成。

在test中常可以看到stub或stubbing,其意思是模擬原方法的行為,例如原方法執行結果返回值為"100",我們可以用stub將原方法的返回值改為"99"。所以在上面無論是stub mock或stub spy的方法,我們可以stub ArrayList.get(100)方法來模擬(自訂)返回的值。

會用stub的時機通常都是單元測試時,被測試方法中呼叫了依賴物件的方法,所以要利用stub來模擬(或偽造)依賴物件方法的行為(及返回值),而不是真的去執行依賴方法的邏輯。只有在整合測試時才會去真的呼叫依賴物件的方法。


參考:

沒有留言:

張貼留言