http://blog.miniasp.com/post/2010/09/18/ASPNET-MVC-Unit-Testing-Part-05-Using-Stub-Object.aspx

前天的文章我介紹了 Mock 假物件 (Fake Object),今天要講另一種假物件稱為 Stub 假物件,這種假物件目的與用途都跟 Mock 非常相近,總之就是為了讓單元測試程式可以順利執行而生的一種開發方式,這兩種假物件類型在單元測試的領域都非常重要,而且各有各的存在必要性,在日後單元測試的日子裡都會經常用到這兩個東西。

我們使用 Mock 技術可以透過介面(Interface)直接無中生有的產生一個假物件,而這類物件可以讓你自由定義該物件應該怎麼執行、傳入什麼資料、回傳什麼預期結果,把物件當成傀儡在用。而 Stub 物件的差別就在於他是真的有個類別定義存在,而這個類別可以被實體化成一個真的物件來用,進而讓測試程式在執行時更能以接近真實的狀況模擬出測試環境 (例如模擬出 HttpContext 的種種物件狀態)。

我們在 ASP.NET MVC 撰寫單元測試,就必須在單元測試的環境下模擬出 ASP.NET MVC 真正執行時的狀態,像是 HttpContext、HttpRequest、HttpResponse、HttpSessionState、Identity、Principal 這些物件都是非常複雜的東西,且牽連的範圍非常廣泛,廣泛到光是撰寫 Mock 就可以耗盡你所有時間,每寫一個測試方法 (Test Method) 都要 Mock 模擬所有的可能結果。

假設你修改了一支之前寫過的 Action 動作方法,修改完後你發現你的單元測試程式執行時卻發生例外,因為你在 Action 動作方法裡用到了一個之前沒有 Mock 到的動作 ( 即執行一個方法 ),為了要讓程式可以正常執行,你又要去修改測試方法裡的 Mock 語法或新增一些 Mock 動作 ( Setup ),在開發單元測試的過程中久而久之你就會覺得你做了許多沒意義的事,因為我們在 Action 裡寫的那些程式碼根本不需要在「單元測試」裡面做任何測試,但卻得每次修改程式都連帶修改測試程式碼,那不是很煩人嗎?

沒錯!這就是 Stub 物件的主要目的,你可以實做某個常用的介面、繼承一般類別或抽象類別實做自己的類別,而這個 Stub 類別每個實做的方法 (Method) 都於原本的類別相容,如此一來,你就可以將此 Stub 類別所建立的物件實體(object instance)傳入要被測試的類別中,在這種情況下,你幾乎不需要再額外做出任何 Mock 的動作,因為這等於你騙被測試的對象他在執行的時候的確擁有而且可以使用這些物件,也因此你的 Stub 類別定義的越真實,相對的你的測試程式就會跑得越順利!

為了證明 Stub 假物件的優異之處,我寫了一個小範例讓大家感受一下,我們來測試在 Global.asax.cs 檔案裡的 RegisterRoutes 方法,用來驗證我們定義的網址路由到底有沒有如我們預期的在運作:

好了,你看到了這段 Code 請問你要如何開始撰寫單元測試呢?而這個 Method 就只有兩行而已,而且 IgnoreRoute 與 MapRoute 這兩個擴充方法都來自其他類別的方法,還記得上一篇文章提到的一句話嗎:「單元測試是軟體測試的最小單位,如果測試的範圍輕易的就會擴展到其他類別或同類別的其他方法,那就不再是最小單位,也就不是單元測試了!」那我們需要測試這兩個方法執行的正確性嗎?這個單元測試我們到底測的是 RegisterRoute、IgnoreRoute 還是 MapRoute 呢?

我們在 瞭解 Mock 假物件 這篇文章的第一個問題有提過一句話:「不應該測試 GetMemberByAccount() 方法執行的正確性!」這句話代表著兩個含意:

  1. 我們不去驗證這段程式的執行結果 ( 例如我們只需要判斷他真的有呼叫過某類別的特定方法 )
  2. 我們相信這段程式的執行結果一定是對的 ( 例如這已經是一個被完整測試過確認無誤的程式 )

以本篇文章的這個例子來說,我們要單元測試的對象是 RegisterRoute,這是是無庸置疑的,但是我們也需要讓 IgnoreRoute 與 MapRoute 真的去執行,而且我們相信這兩個方法的執行結果一定會如我們預期的回傳正確的結果,在這個前提之下或許有點大膽,不過如果你沒有這個前提,你根本無法進行測試,或是你只能進行無意義的測試,例如你只驗證在 RegisterRoute 方法裡必須要執行 MapRoute 方法,這也太瞎了吧!

以我們對 ASP.NET MVC 的瞭解,我們知道當瀏覽器發出 HTTP 要求並指定 URL 取得資源時會經過 Routing 這一段,而在執行過 RegisterRoute 方法後我們會得到一組 RouteData 資料,裡面包含了一堆 RouteValueDictionary 的集合資料,例如包括 controller、action 等等之類的路由值(RouteValue)。

這時問題來了,第一,我們不瞭解 IgnoreRoute 與 MapRoute 的實做細節,基於物件導向程式開發的開放封閉原則 (Open/closed principle;OCP) 我們也不應該去瞭解 IgnoreRoute 與 MapRoute 的實做細節,我們只要知道怎麼使用它就好了。第二,由於這是 Web 環境,我們要在單元測試環境下必須模擬出非常多可能的變數,事實上,這個變數就是 HttpContext 類別,但是這個類別又再牽扯的相關類別多到程式碼印出來可以環繞地球三圈,以下是用 NDepend 分析出來所有與 HttpContext 類別相關的所有類別,紅色是HttpContext 類別的程式碼,藍色部分就是所有相關的:

由於我們不瞭解 IgnoreRoute 與 MapRoute 的實做細節,所以我們不可能知道到底需要 Mock 多少物件,我們也很難寫出一個「可信任」的單元測試程式。因此,我們需要一個完整的 Stub 物件,幫助我們讓 IgnoreRoute 與 MapRoute 都能正確無誤的執行,並回傳如預期的結果!

由於 ASP.NET MVC 擁有非常優秀的可測試性,除此之外,也已經有不少人貢獻了許多方便單元測試的 Stub 類別與輔助方法,所以我們不用重新發明輪子,直接使用 MvcContrib 專案的 Unit Testing Library (TestHelper ) 即可擁有許多非常好用的 Stub 類別。

我們先下載 MvcContrib 的組件回來:

解壓縮之後找出以下三個組件,並加入到你的測試專案中:

接著我們新增一個 MvcApplicationTest.cs 測試類別:

然後再類別裡寫一個測試方法:

如上程式碼依序號解釋如下:

  1. 我們先取得一個空的 RouteCollection 以便傳入 MvcApplication.RegisterRoutes 執行
  2. 利用 MvcContrib 專案的 Unit Testing Library 內建的 FakeHttpContext 類別得到一個 Stub 物件 
    註: 這時會傳入模擬的 URL 與 HTTP Method ( 如 GET 或 POST )
  3. 執行 MvcApplication.RegisterRoutes 方法 ( 這時我們會得到被設定過 Routing 資料的 route 物件 )
  4. 執行 routes.GetRouteData 方法並傳入 context (Stub 物件) 以運算出接近真實的 RouteData 物件
  5. 最後驗證 RouteValue 是否如預期的正確

執行測試,得到正確的結果:

如果我們沒有 FakeHttpContext 類別幫我們產生 Stub 物件,我想幾乎是沒辦法測試 Routing 機制的,這個 FakeHttpContext 類別還沒那麼單純,若沒有另外再模擬出 FakeHttpRequest、FakeHttpRequest、FakeHttpResponse、FakeHttpSessionState 這些 Stub 的話,事實上還是不夠完整的,我們可以利用 Reflector 工具查看相關程式與命名空間:

由以上描述應該可以得知 Mock 與 Stub 的主要差異了吧,我再次大膽的將 Mock 與 Stub 二分如下:

  • Mock 物件適用於較為簡易的測試情境
  • Stub 物件適用於模擬較為複雜的情境資訊 (Context Information)

這樣二分法其實並不好,因為 Stub 物件也可以用來模擬簡易的測試情境,Mock 也可以用來模擬複雜的情境,所以這份拿捏並不容易,不過你也可以參考以下意見進行判斷:

  • 建立 Mock 物件的寫法比較簡單、直覺,不用預先建立類別,撰寫的速度非常快,節省時間
  • 建立 Stub 物件需要花較長的時間撰寫相關程式,需完整瞭解被測試程式的運作細節才能寫出適合的 Stub 類別

另一個從程式的外觀上的差異是:

  • Mock 不用先建立物件即可透過 DynamicProxy 的機制動態生成類別與自動建立物件實體
  • Stub 需預先建立可模擬真實狀況的類別,並且需要在測試程式中明確建立物件實體

---

單元測試首重觀念正確,否則測試程式會越寫越灰心的,當然,我也不敢保證我說的一定正確,但至少我寫的這些觀念都是我深思熟慮過後的產物,歡迎各位留言探討問題或思路打結的地方,我將盡可能回答各位的問題,如有謬誤之處也請放心指教,謝謝。

程式碼下載

  • MvcApplication5.zip

ASP.NET MVC 單元測試系列 (5):瞭解 Stub 假物件相关推荐

  1. ASP.NET MVC 單元測試系列 (3):瞭解 Mock 假物件 ( moq )

    http://blog.miniasp.com/post/2010/09/16/ASPNET-MVC-Unit-Testing-Part-03-Using-Mock-moq.aspx 我們在上一篇已經 ...

  2. ASP.NET MVC教程六:两个配置文件详解

    前言 在新建完一个MVC项目之后,你会发现整个整个项目结构中存在有两个web.config文件,如下图所示: 这两个配置文件,一个位于项目的根目录下面,一个位于Views文件夹下面,这两个配置文件有什 ...

  3. ASP.NET MVC 入门系列教程

    一个居于ASP.NET MVC Beta的系列入门文章,有朋友提议说写一个示例程序来同步讲解,那样更加容易学习.所以就写选择了写一个Blog程序来作为示例程序.(原来是居于ASP.NET MVC Pr ...

  4. Asp.net MVC 教程汇总

     自学MVC看这里--全网最全ASP.NET MVC 教程汇总 MVC架构已深得人心,微软也不甘落后,推出了Asp.net MVC.小编特意整理博客园乃至整个网络最具价值的MVC技术原创文章,为想 ...

  5. ASP.NET MVC 教程学习

    1. Why :为什么需要ASP.NET MVC 本章主要为大家汇总了为什么学习Asp.net MVC替代WebForms,产生ASP.NET MVC 的需求是什么,只有更好的理解了为什么需要MVC, ...

  6. ASP.NET MVC使用Bootstrap系列(3)——使用Bootstrap 组件

    Bootstrap为我们提供了十几种的可复用组件,包括字体图标.下拉菜单.导航.警告框.弹出框.输入框组等.在你的Web Application中使用这些组件,将为用户提供一致和简单易用的用户体验. ...

  7. 我的crystal report for asp.net測試通過了

        昨天晚上﹐測試在vs.net2003中用推的模式呼叫crystal report﹐成功﹐今天一大早﹐ 又開始繼續測試其轉檔與參數傳遞的功能﹐花了2個小時﹐都已OK,綜合這几天的測試﹐    我 ...

  8. ASP.NET MVC实践系列11-FCKEditor和CKEditor的使用

    FCKEditor是一款强大的在线编辑器,简单实用,多浏览器兼容,免费开源,应用十分广泛,据他的官方网站上称有三百多万的下载量,而且无数的知名大公司正在使用它.所以FCKEditor是很值得信赖的,现 ...

  9. [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序处理并发

    这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第十篇:为ASP.NET MVC应用程序 ...

最新文章

  1. 重磅直播|激光雷达在高精地图中的应用
  2. 微软云计算 Windows Azure“腾云驾雾”
  3. BootStrap 效果展示
  4. 打包文档_苏教版小学数学16年级全十二册教案Word文档打包下载
  5. JavaEE课程目标、个人目标、互联网应用和企业级应用的区别
  6. 机器学习相关知识 大佬博客整理
  7. 掩码图制作photoshop__新手用
  8. python可变参数函数二阶导数公式_Python中函数的参数定义和可变参数
  9. 【论文写作】SSM校园招聘系统如何写摘要部分
  10. nginx+php5-fpm安装
  11. 浅谈静态方法与静态变量
  12. 今天开始学习ADO.NET中的Connection对象(一)--SqlConnection对象连接SQL Server
  13. 非旋Treap——维护数列
  14. 关于PhpDE zend ide破解方式
  15. SNAT、DNAT、MASQUERADE的区别
  16. MAC表、ARP缓存表、路由表以及端口映射NAT
  17. 微信向移动开放平台又迈进了一大步:微信开放平台更新
  18. 二值化及伽马调整的函数
  19. Spring Boot 注解原理
  20. 2023年国内所有期刊复合、综合影响因子等35指标数据库

热门文章

  1. 如何安装scratch
  2. 星系超级计算机,超级计算机创造了数以百万计的虚拟宇宙来了解星系的进化
  3. 丝绸之路(Silk Road)3
  4. 手把手教你用Smartbi制作:常用报表之分栏报表
  5. 充电桩检测仪表TK4860E交流充电桩检定装置
  6. 平流式沉淀池流量计算_平流沉淀池计算公式 高版本格式
  7. ADC的相关知识整理
  8. 四边形顶点坐标排序十字分割法
  9. 如何一次获取多个Word文档的页数
  10. JAVA版大型通用WMS仓库管理系统源码 WMS系统源码