Mock 入門,分析stub . mock區別

面向接口編程的測試難的問題

Mock Framework的用處在於咱們能夠在不實現具體對象的狀況下,即在沒有某個類的實例的狀況下對該對象的行爲進行模擬。這一特徵對於面向接口的編程很是有用。由於接口的調用者能夠在沒有接口的具體實現的狀況下使用接口,也就是說調用者能夠先於接口的實現者行動。也許有人以爲這好像沒什麼神奇的,即便沒有mock我也同樣可使用接口啊,但是我要問:編程

  「在沒有接口實現的狀況下,你能對調用接口的代碼進行測試嗎?」框架

「NullReferenceException」相信不少人都碰到過的吧。因爲接口不能定義構造函數,也就沒法實例化,致使了調用接口的代碼沒法運行,固然也就是沒法測試。函數

Mocking能幹什麼?

從mock 的字面意思就能夠了解一二了,它的主要工做是模擬出一個被模擬對象的實例,其中包括模擬對該實例的調用行爲(好比訪問屬性、調用方法之類)、模擬方法或屬性訪問的返回值、模擬方法和索引的參數傳遞等等,能夠說基本上對於一個對象實例的使用它均可以模擬出來。這樣一來,咱們就能夠好像真的有一個咱們須要的實例存在同樣,正常地使用它,來完成對調用者代碼的開發和測試。單元測試

Mock object和stub object同樣嗎?

固然不同!寫過stub測試程序的人應該知道,stub是真是對象的一個模擬,好比調用者須要一個值,那就讓stub輸出一個值,若是調用者須要傳遞一個值給stub,那就在stub中定義一個方法接受該參數。可是這與mock的對象存在本質的區別:測試

stub雖說也是模擬,但其本質上對真是對象的一個簡單實現,而不管它有多簡單它都是一種實現,它是真是存在的,它裏面包含了咱們定義的操做代碼;網站

反觀mock的對象,它根本是不存在的,哪怕一句的簡單的不能再簡單的代碼都不存在。spa

 

在理解其區別以前,須要明白一點,他們都是爲了同一個目標而出現的,代替依賴部分,讓原先的「整合測試」簡化爲「單元測試」。       3d

mock:使用easymock等包,在程序代碼中向被測試代碼注入「依賴部分」,經過代碼可編程的方式模擬出函數調用返回的結果。對象

stub:本身寫代碼代替「依賴部分」。它自己就是「依賴部分」的一個簡化實現。索引

     實際上,在可以使用mock的時候,就不該該選擇使用stub。可是有時候是必須使用stub的,例如在對遺留代碼進行測試時,該部分代碼不支持「注入」,那麼只能將「替代」這個過程外移,使用stub完成此任務了。

 

應用場景

就以我如今正在開發這個網站代碼爲例,來講一下若是在測試的使用Mock object.如今有一個需求,咱們須要根據給定的搜索關鍵字和搜索範圍來進行項目的搜索,以MVP的方式實現的話咱們定義了一個IView接口:

  public interface IView_SearchProject
  {
    void AttachPresenter(Presenter_SearchProject presenterSearchProject);
    SearchRange Range { get;}
    string SearchKey { get;}
    string UrlBase { get;}
    void NavigateTo(string searchUrl);
  }

以及一個Presenter:

public class Presenter_SearchProject
  {
    public Presenter_SearchProject(IView_SearchProject viewSearch)
    {
        view = viewSearch;
        range = view.Range;
        prjNav = new ProjectSearchNavigator(view.UrlBase);
        query = new SearchQuery();
    }

    public string GetDesUrl()
    {
        query.WithDescription = range.WithDescription;
        query.WithName = range.WithName;
        query.WithKey = range.WithKey;
        query.SearchKey = view.SearchKey;
        query.Ids = range.Ids;

        prjNav.Compile(query);
        return prjNav.DestUrl;
    }

    public void Search()
    {
        view.NavigateTo(GetDesUrl());
    }

    private IView_SearchProject view;
    private SearchRange range;
    private ProjectSearchNavigator prjNav;
    private SearchQuery query;
  }

ProjectSearchNavigator是一個實現頁面跳轉的幫助類,負責根據View(這裏是一個aspx的頁面)傳遞的搜索關鍵字SearchKey和querystring構造出搜索頁面的地址。SearchQuery類負責解析Request.QueryString集合,由於其中存儲的key/value對,須要據此構造出全部查詢條件的一個字符串。
Mocking and Testing

Mocking說到底多試爲了測試,不然咱們沒有必要,由於mocking出來的對象並不能做爲的真是的代碼運行。先把測試的代碼貼出來,再進行解釋,但願你不要以爲太多了:)

  [TestFixture]
  public class Presenter_SearchProject_Test
  {
    [SetUp]
    public void SetUp()
    {
        mockRepository=new MockRepository();//1
        mockView = mockRepository.CreateMock<IView_SearchProject>();//2
    }
    [Test]
    public void GetDestUrl()
    {
        SearchRange range = new SearchRange(true, true, false, string.Empty);
        //3
        //
        Expect.Call(mockView.Range).Return(range) ;
        //UrlBase
        Expect.Call(mockView.UrlBase).Return("http://localhost");
        //SearchKey
        Expect.Call(mockView.SearchKey).Return("searchKey");
        
        //4
        mockRepository.ReplayAll();
        //5
        presenter = new Presenter_SearchProject(mockView);

        string destUrlReturned = presenter.GetDesUrl();
        string destUrlExpected = "http://localhost/ProjectPage/ProjectControl.aspx?"
                  +"search=searchKey&name=True&key=True&description=False";
        //6
        Assert.AreEqual(destUrlExpected,destUrlReturned);
    }

    IView_SearchProject mockView;
    MockRepository mockRepository;
    Presenter_SearchProject presenter;

    [TearDown]
    public void TestCleanup()
    {
        mockRepository.ReplayAll();
        mockRepository.VerifyAll();
    }

  }

  1. Rhion.Mock框架中要使用mock的對象都須要從MockRepository 這個對象中產生,它充當一個對象工廠的角色。
  2. 這一步就是建立咱們使用的mock的對象了,須要以被mock類的類型做爲泛型參數。
  3. 這一步的3行代碼是真正mock的部分,它們分別對應着對mockView的三次調用。
    Expect.Call(mockView.Range).Return(range) ;
    Expect.Call表示咱們但願調用mockVIew的那個方法,也包括屬性。

    .Return的意思咱們打算讓這個模擬對象返回什麼樣的值

    綜合起來的意思就是:咱們但願mockView的調用者在調用MockView的某一個方法(或屬性)時返回一個有return標識的值
  4. ReplayAll的調用千萬不要忘掉,它的意思能夠理解爲,讓以前設定的模擬行爲生效,今後以後咱們就能夠把這個mock的對象看成是一個真是的對象來使用了。我以爲能夠把它想像成CLR爲咱們自動生成了代碼同樣,爲咱們生成了一個對被mock對象的實現。
  5. 這一步是調用者對mock對象的使用。
  6. 測試咱們關注的對象的行爲是否正常

一個須要注意到地方

presenter = new Presenter_SearchProject(mockView);

像這樣的初始化須要注意順序,必需要等到MockView被真正模擬出來以後,也就是ReplayAll調用以後,由於在presenter 內部須要訪問mockView的成員,好比:

range = view.Range;

可是若是你在mock對象調用者初始化的時候沒有訪問mock對象的成員,那麼這樣的初始化能夠的。由於雖然mock對象的成員還米有mock出來,可是mock對象已經被生成了:

mockView = mockRepository.CreateMock<IView_SearchProject>();

只不過是個空殼:)

相關文章
相關標籤/搜索