解析ASP.NET MVC項(xiàng)目中視圖的單元測試_.Net教程
推薦:談.NET平臺(tái)編程語言的未來在去年的PDC2008召開期間,微軟逐步公開了圍繞.NET和編程語言的很多想法,據(jù)此我們可以饒有興趣地對.NET的未來預(yù)測一番。 .NET平臺(tái)以運(yùn)行在通用語言運(yùn)行時(shí)(Common Language Runtime,CLR)上的C#和VB.NET作為開端。CLR是通用語言架構(gòu)(Common Language Infrast
關(guān)于視圖的單元測試
說到ASP.NET MVC,我們似乎始終都在關(guān)注對于Controller的測試——雖然Stephen Walther也寫過如何脫離Web Server對View進(jìn)行單元測試,但是他的方法可看而不可用。復(fù)雜的構(gòu)造和預(yù)備,以及對生成的HTML字符串作判斷——這真是在對視圖做單元測試嗎?仔細(xì)分析他的代碼可以發(fā)現(xiàn),這其實(shí)是在對ViewEngine做單元測試。而且,如果真要對ViewEngine做單元測試,也不應(yīng)該像他那樣依賴外部文件。在我看來,他的做法什么都不是……似乎美觀,似乎能博得一些“掌聲”,但是這個(gè)掌聲是來自于他的解決方案,還是大家一時(shí)的沖動(dòng)呢?
如果要對視圖做單元測試,還是要將內(nèi)容呈現(xiàn)在瀏覽器中才行。在對網(wǎng)頁做單元測試時(shí),我們一般會(huì)使用WatiN等工具操作瀏覽器,打開頁面,再對其DOM元素結(jié)構(gòu)及內(nèi)容作斷言。不過……這是單元測試嗎?可惜這只能算是一種回歸測試或用戶驗(yàn)收測試。因?yàn)�,我們在打開一個(gè)頁面的時(shí)候,從表現(xiàn)層到業(yè)務(wù)邏輯再到數(shù)據(jù)訪問,應(yīng)用程序的每個(gè)部件都在忙碌著。而單元測試講究的是“分離”,分離一切關(guān)注,分離一切依賴。因?yàn)榉蛛x,我們才能準(zhǔn)確定位錯(cuò)誤;因?yàn)榉蛛x,我們才能在測試中使用我們準(zhǔn)備好的數(shù)據(jù)。
既然要分離,我們就必須遵循一定的使用規(guī)范。在《ASP.NET MVC單元測試最佳實(shí)踐》中我提到,在View中只能使用ViewData中的數(shù)據(jù),而不該依賴其他內(nèi)容(包括HttpContext)。這樣我們就可以自行構(gòu)造ViewData并注入一個(gè)視圖對象中。事實(shí)上,這個(gè)約定在ASP.NET MVC自帶的項(xiàng)目模板中就被破壞了。請看Views\Shared\LogOnUserControl.ascx,其中通過this.User來查看當(dāng)前用戶的登陸狀態(tài)。這是個(gè)定義在傳統(tǒng)Page對象上的屬性,從當(dāng)前HttpContext上直接獲取。如果使用這種方式,我們在單元測試時(shí)就難以“模擬”當(dāng)前用戶的登陸狀態(tài),進(jìn)而難以使測試覆蓋到測試的各種情況了。
Lightweight Test Automation Framework
在這里,老趙推薦使用ASP.NET Team提供的Lightweight Test Automation Framework(下文稱之為LTAF)作為測試工具,它目前已經(jīng)在CodePlex上更新至Feb Update版本。這個(gè)框架的作用與WatiN和Selenium類似,可操作瀏覽器對應(yīng)用程序編寫回歸測試。雖然在某些方面(例如DOM元素的選�。┎蝗�“競爭對手”,但是LTAF自有其獨(dú)到之處:
由于直接在瀏覽器中運(yùn)行,它天生便支持現(xiàn)有的——以及未來可能出現(xiàn)的任意瀏覽器。
由于直接部署在被測試的網(wǎng)站中,因此測試代碼和網(wǎng)站頁面是在同一個(gè)進(jìn)程中。
第一點(diǎn)優(yōu)勢自不必說,而第二點(diǎn)更是關(guān)鍵。試想WatiN和Selenium,都是通過編寫代碼在瀏覽器中打開頁面。這意味著我們的在測試代碼和被測試的網(wǎng)頁分別在不同的進(jìn)程中。在這個(gè)前提下,如果我們要將測試代碼中定義的數(shù)據(jù)傳遞給被測試的網(wǎng)頁(也就是視圖對象),我們就必須進(jìn)行跨進(jìn)程的通信。而無論怎么實(shí)現(xiàn),都逃不過“序列化”一途,這無疑增加了復(fù)雜度。而使用LTAF之后,這個(gè)問題瞬間煙消云散了,因?yàn)槲覀兛梢灾苯釉趦?nèi)存中“傳遞”測試數(shù)據(jù),一切都只是個(gè)引用而已。
不過任何事物都具有兩面性,LTAF也有一些難以天生的,而且是永遠(yuǎn)無法彌補(bǔ)的缺點(diǎn)。例如:
由于LTAF將待測試的頁面放置在Frame中,因此該頁面上的window.top等基于瀏覽器frame結(jié)構(gòu)的屬性會(huì)被改變。
由于LTAF的本質(zhì)是使用JavaScript來操作DOM,這意味著任何會(huì)阻塞程序進(jìn)行的操作(例如alert)都不能使用,否則將阻塞整個(gè)測試過程。
不過幸運(yùn)的是,這兩點(diǎn)都不回成為嚴(yán)重的問題。對于第一種,我們只需要編寫一個(gè)自定的getTop方法來替換直接訪問windows.top的做法即可。而第二種情況——老趙從來不喜歡alert或confirm這種“純?yōu)g覽器功能”,因?yàn)樗鼈儠?huì)帶來很差的用戶體驗(yàn),更何況現(xiàn)在的JavaScript類庫/框架都能很輕松的做出這種效果,您覺得呢?
LTAF的具體使用方式可參考其Release Note。令人奇怪的是,老趙發(fā)現(xiàn)直接在項(xiàng)目中使用LTAF會(huì)有一些小問題(不過它的示例為什么就一切正常呢?),因此進(jìn)行了一些細(xì)微的修改。請注意~\UnitView\DriverPage.aspx文件尾部的一些JavaScript代碼。
UnitView的使用
于是老趙編寫了一個(gè)組件UnitView,方便我們構(gòu)造一個(gè)單元測試時(shí)所需的數(shù)據(jù)。有了數(shù)據(jù),便能夠直接將視圖在瀏覽器中加以呈現(xiàn)了。例如:
[WebTestClass]
public class HomeTests
{
[WebTestMethod]
public void LoggedOnIndexTest()
{
var data = new TestViewData
{
ControllerName = "Home",
ActionName = "Index",
Model = new IndexModel
{
Message = "Welcome guys!",
Identity = new UserIdentity
{
IsAuthenticated = true,
Name = "Jeffrey Zhao"
}
}
};
HtmlPage page = new HtmlPage(TestViewData.GenerateHostUrl(data));
// Assert title
Assert.AreEqual("Home Page", page.Elements.Find("title", 0).GetInnerText());
// Assert head element
var mainContent = page.Elements.Find("main");
var head2 = mainContent.ChildElements.FindAll("h2").Single();
Assert.AreEqual(data.Model.Message, head2.GetInnerText(), "Message should be displayed.");
var loginTabInnerText = page.Elements.Find("logindisplay").GetInnerTextRecursively();
Assert.IsTrue(loginTabInnerText.Contains("Welcome"), "'Welcome' missed.");
Assert.IsTrue(loginTabInnerText.Contains(data.Model.Identity.Name), "Login name missed.");
}
}
自然,Web Server是不可或缺的。幸運(yùn)的是,分離讓我們的視圖只會(huì)涉及最簡單的測試數(shù)據(jù),這樣VS自帶的簡單Web Server就足夠了。在上面的代碼中,我們直接構(gòu)造了強(qiáng)類型的TestViewData對象,它包含呈現(xiàn)一個(gè)視圖所需要的所有數(shù)據(jù):
Cotroller和Action名稱。從理論上說,由不同的Controller和Action進(jìn)入同樣的視圖可能會(huì)得到不同的結(jié)果。
View和Master名稱。如果省略,則表明將使用默認(rèn)的視圖,即通過Controller和Action的值來確定。
ViewData和Model。
TestViewData.GenerateHostUrl方法會(huì)把data保存起來,并返回一個(gè)URL。訪問該URL便能夠得到對應(yīng)的視圖內(nèi)容。
如果您想使用UnitView,可以從上面的鏈接中下載UnitView的源代碼和示例在本機(jī)進(jìn)行嘗試。使用UnitView時(shí)主要有以下幾個(gè)注意點(diǎn):
將Tests項(xiàng)目的輸出路徑指向被測試網(wǎng)站的bin目錄,這樣既可以在運(yùn)行時(shí)得到正確的程序集,又不必為網(wǎng)站添加多余的引用。
將~\UnitView目錄復(fù)制到您的網(wǎng)站根目錄下(在發(fā)布網(wǎng)站時(shí),請?zhí)蕹撃夸洠�。如果想使用其它目錄,請關(guān)注接下來UnitView實(shí)現(xiàn)分析。
編輯~\UnitView\Web.config文件,將MvcApp.Tests.dll修改為您自己的包含測試代碼的程序集。
UnitView實(shí)現(xiàn)分析
UnitView組件非常簡單,簡單地幾乎不值一提。TestViewData類型包含了測試需要的所有數(shù)據(jù),而TestViewData繼承了TestViewData,提供了強(qiáng)類型的Model屬性訪問方式。它們就不作分析了。
分享:ASP.NET如何防止用戶多次登錄常見的處理方法是,在用戶登錄時(shí),判斷此用戶是否已經(jīng)在Application中存在,如果存在就報(bào)錯(cuò),不存在的話就加到Application中(Application是所有Session共有的,整個(gè)web應(yīng)用程序唯一的一個(gè)對象): 以下是引用片段: string strUserId = txtUser.Text; ArrayLi
- asp.net如何得到GRIDVIEW中某行某列值的方法
- .net SMTP發(fā)送Email實(shí)例(可帶附件)
- js實(shí)現(xiàn)廣告漂浮效果的小例子
- asp.net Repeater 數(shù)據(jù)綁定的具體實(shí)現(xiàn)
- Asp.Net 無刷新文件上傳并顯示進(jìn)度條的實(shí)現(xiàn)方法及思路
- Asp.net獲取客戶端IP常見代碼存在的偽造IP問題探討
- VS2010 水晶報(bào)表的使用方法
- ASP.NET中操作SQL數(shù)據(jù)庫(連接字符串的配置及獲取)
- asp.net頁面?zhèn)髦禍y試實(shí)例代碼
- DataGridView - DataGridViewCheckBoxCell的使用介紹
- asp.net中javascript的引用(直接引入和間接引入)
- 三層+存儲(chǔ)過程實(shí)現(xiàn)分頁示例代碼
.Net教程Rss訂閱編程教程搜索
.Net教程推薦
- 編寫ASP.NET應(yīng)用程序的技巧
- 淺析asp.net驗(yàn)證控件失效原因及解決方法
- ASP.Net網(wǎng)絡(luò)數(shù)據(jù)庫:連接到數(shù)據(jù)庫
- ASP.NET MVC :實(shí)現(xiàn)我們自己的視圖引擎
- 怎樣實(shí)現(xiàn)MSMQ的消息加密
- 基于C#的接口基礎(chǔ)教程之七
- asp.net中倒計(jì)時(shí)自動(dòng)跳轉(zhuǎn)頁面的實(shí)現(xiàn)方法(使用javascript)
- 基于C#的接口基礎(chǔ)教程之四
- 一個(gè)功能齊全的DataGrid分頁例子
- 深入Lumisoft.NET實(shí)現(xiàn)郵件發(fā)送功能的方法詳解
- 相關(guān)鏈接:
- 教程說明:
.Net教程-解析ASP.NET MVC項(xiàng)目中視圖的單元測試
。