在Visual Studio中,Coded UI Test已經不是什么新特性了,較早版本的Visual Studio中就已經有這個東東了。它主要用來幫助自動化測試工程師和開發人員確保程序在UI方面沒有任何問題。這其中包含了豐富的內容。在這之前,我一直對自動化測試的工作以及什么是自動化測試一知半解,具備自動化測試編碼能力的工程師所掌握的技能在某種程度上要遠超程序開發人員和設計人員,對於這一點,我早有耳聞!但直到親身體驗我才確信,測試工作遠沒有我們想象得那么簡單。開發人員或許花上數小時就可以完成項目中某一個獨立模塊並使其在一定范圍內正常運行,然而,自動化測試工程師也許會花上好幾天的時間來編寫對應的自動化測試代碼來確保這一功能運行正常。
Coded UI Test包含了十分豐富的API庫,它可以錄制和回放UI操作,捕捉UI元素並獲取屬性的值,並生成操作代碼。測試人員在生成代碼的基礎上對測試對象的值進行邏輯判斷並給出測試結果。創建一個Coded UI Test很容易,大多數情況下,我們只需要借助於Visual Studio就可以完成絕大部分操作。為了說明整個操作過程,我們假設一個測試需求:
在瀏覽器中打開百度搜索,輸入“jaxu cnblogs”關鍵字,搜索並查看結果的第一條是否為“Jaxu - 博客園”
基本操作
(本文演示的所有代碼和操作均在Visual Sutdio 2013和Windows 8.1 + IE 11環境下)
在Visual Studio中開始創建一個Coded UI Test Project。這很簡單!
工程創建成功后,Visual Studio會問你是馬上開始一個新的UI錄制還是選擇已經錄制好的操作。當然你也可以選擇取消,在后面的步驟里再開始UI錄制。
工程默認生成CodedUITest1.cs文件。在開始錄制UI操作之前,對基本概念做一下介紹:
- Coded UI Test工程的運行是從包含有[CodedUITest]特征屬性的類開始的。一個工程中可以有多個這樣的類。
- 與普通的工程不同,我們不能通過F5或者點擊Visual Studio中的運行按鈕來調試或直接運行工程,Coded UI Test工程必須通過Test Explorer或者在包含有[CodedUITest]特征屬性的類中來選擇運行相應的測試方法。
- 在帶有[CodedUITest]特征屬性的類中,所有的測試方法都必須帶有[TestMethod]特征屬性,以表示它是一個有效的測試方法,可以直接運行。
- 通過選擇TEST->Windows->Test Explorer可以打開Test Explorer窗口,在Test Explorer窗口中可以查看工程中所有的測試方法並選擇運行。當然,你也可以在帶有[TestMethod]特征屬性的測試方法的代碼塊中右鍵選擇運行該測試方法。
- 測試方法同樣可以調試。在選擇運行測試方法時,你會看到有Debug Test的菜單,調試的過程和在普通工程中一樣。
然后,我們開始一個UI錄制。在工程中添加一個Coded UI Test Map文件。創建成功后Visual Studio會自動在屏幕的右下角打開Coded UI Test Builder窗口,以方便我們進行UI錄制操作。
借用MSDN上的圖片來對Coded UI Test Builder窗口上按鈕的功能做一下簡單的說明:
- Record icon - 開始一個UI操作的錄制。例如菜單導航、按鈕點擊等操作。
- Edit steps - 對錄制的步驟進行編輯,調整或者刪除冗余的步驟。
- Add assertions - 不僅僅是添加斷言,通過點擊該按鈕並拖放到測試對象的UI上以選取控件,然后你可以添加斷言。
- Generate code - 這一步很關鍵,在完成以上所有的操作后,通過點擊該按鈕Visual Studio會自動為你生成代碼。生成的代碼在.uitest文件下面的.Desinger.cs文件中可以看到。注意不要手動修改自動生成的代碼,這會導致下次通過Coded UI Test Builder窗口對.uitest文件進行修改時某些對象或操作不同步。
- Close to finish recoding - 在關閉Coded UI Test Builder窗口之前,確保所做的修改已經生成了對應的代碼。如果要修改.Designer.cs文件中自動生成的代碼,可以在Solution Explorer中右鍵選擇.uitest文件,然后選擇Edit With Coded UI Test Builder。在后面的步驟中我們會講到這一點。
UI Action的錄制和UI控件的選擇操作是分開的。讓我們先開始UI Action的錄制。
- 點擊Coded UI Test Builder窗口中的紅色按鈕
- 打開IE瀏覽器,在地址欄中輸入http://www.baidu.com以導航到百度搜索引擎
- 輸入關鍵字“jaxu cnblogs”,點擊“百度一下”
- 在Coded UI Test Builder窗口中點擊Pause,然后點擊Edit steps,刪除不必要的步驟。我刪除了操作中的第4步和第5步。
- 選擇Generate code,對所要生成的方法取個名字,然后點擊Add and Generate按鈕。Visual Studio為我們所錄制的UI操作步驟生成了對應的代碼。
- 關閉Coded UI Test Builder窗口,在Visual Studio中查看剛才生成的代碼。
在Solution Explorer中展開UIMap1.uitest文件,選擇並打開UIMap1.Designer.cs文件,可以看到剛才所生成的代碼。是不是很想現在就運行一下,來看看這些自動生成的代碼如何運行?現在還不行,因為單純的UI Action運行沒有任何意義,Coded UI Test的真正意義是通過UI操作來定位到UI上的某一個特定元素,並最終通過斷言來確定該元素的屬性是否和預期的值相等。
為了能夠手動修改.Designder.cs文件中生成的代碼,我們需要將它們移到.cs文件中。在Solution Explorer中雙擊UIMap1.uitest文件,在打開的窗口中我們可以看到左邊是UI Actions所生成的步驟,右邊是UI Control Map(稍后我們會用到它)。在左邊的UI Actions中選擇根節點RecordedMethod1,然后在頂部的菜單中選擇Move code to UIMap1.cs,代碼會被移到.cs文件以方便我們進行修改。完成該步驟之后,我們可以在.cs文件中看到這些代碼並做相應的修改。
你可能已經注意到了,自動生成的代碼中有些對象的名字看起來並不那么好,甚至有些還包含了中文。你希望修改它們,但是不要在.Designer.cs文件中做任何修改!還記得前面我們講過的Edit With Coded UI Test操作嗎?在Solution Explorer中右鍵選擇UIMap1.uitest文件,右鍵選擇Edit With Coded UI Test打開Coded UI Test Builder窗口,然后點擊Add assertions按鈕(就是那個用來選擇UI Control的按鈕),然后展開UI Control Map界面。如下圖,我們可以對其中生成的UI Controls進行編輯和重命名。
完成修改之后再次點擊Generate code按鈕並關閉Coded UI Test Builder窗口,此時.Designer.cs文件中自動生成的代碼已經做了修改。由於前面我們已經將相關的UI Actions部分的代碼移到.cs文件里了,所以重命名的對象我們還需要在.cs文件中手動進行修改,否則編譯時會出錯。建議在將代碼移到.cs文件之前完成自動生成代碼的修改工作,以避免手動修改過多的代碼。
然后我們需要捕捉到百度搜索結果的UI控件,並對其中的結果進行判斷。仍然使用Coded UI Test Builder窗口。
- 打開瀏覽器並導航到百度,輸入關鍵字“jaxu cnblogs”並點擊“百度一下”
- 在Coded UI Test Builder窗口中點擊Add assertions按鈕並拖放到瀏覽器窗口,同時指向你想要捕捉的控件上。有些控件很難精確捕捉到,沒有關系,你可以先選中臨近的控件,然后使用Coded UI Test Builder窗口中的方向按鈕移動到你所要定位的元素。如下圖,我們選擇並定位到了百度搜索結果的DIV元素,並將該控件命名為UIContent_leftPane。
- 在Coded UI Test Builder窗口的左上角點擊添加選擇的控件,這一點很重要!忘記這一步則所選擇的控件不會被添加到生成的代碼中。
- 生成代碼並關閉Coded UI Test Builder窗口。
至此,所有的UI Actions和UI Controls都已經定義完畢,接下來我們要編碼以完成對搜索結果的判斷。借助於自動生成的代碼,我們編寫了下面的測試方法以實現文章最開始的測試需求。
namespace CodedUITestProject2.UIMap1Classes { using Microsoft.VisualStudio.TestTools.UITesting.HtmlControls; using Microsoft.VisualStudio.TestTools.UITesting.WinControls; using System; using System.Collections.Generic; using System.CodeDom.Compiler; using Microsoft.VisualStudio.TestTools.UITest.Extension; using Microsoft.VisualStudio.TestTools.UITesting; using Microsoft.VisualStudio.TestTools.UnitTesting; using Keyboard = Microsoft.VisualStudio.TestTools.UITesting.Keyboard; using Mouse = Microsoft.VisualStudio.TestTools.UITesting.Mouse; using MouseButtons = System.Windows.Forms.MouseButtons; using System.Drawing; using System.Windows.Input; using System.Text.RegularExpressions; public partial class UIMap1 { public void TestSearchResult() { HtmlDiv resultPanel = this.UINewtabInternetExplorWindow.UIJaxucnblogs_SearchDocument.UIContent_leftPane; HtmlDiv resultPanelFirst = (HtmlDiv)resultPanel.GetChildren()[0]; HtmlHyperlink link = new HtmlHyperlink(resultPanelFirst); Assert.AreEqual("Jaxu - 博客園", link.InnerText, "Validation is failed."); } /// <summary> /// RecordedMethod1 - Use 'RecordedMethod1Params' to pass parameters into this method. /// </summary> public void RecordedMethod1() { #region Variable Declarations WinEdit uIItemEdit = this.UINewtabInternetExplorWindow.UIItemWindow.UIItemEdit; HtmlEdit uIWDEdit = this.UINewtabInternetExplorWindow.UIDocument.UIWDEdit; HtmlInputButton uISearchButton = this.UINewtabInternetExplorWindow.UIDocument.UISearchButton; #endregion // Go to web page 'about:Tabs' using new browser instance this.UINewtabInternetExplorWindow.LaunchUrl(new Uri("http://www.baidu.com")); // Type 'www.baidu{Enter}' in text box //Keyboard.SendKeys(uIItemEdit, this.RecordedMethod1Params.UIItemEditSendKeys, ModifierKeys.None); // Type 'jaxu cnblogs' in 'wd' text box uIWDEdit.Text = this.RecordedMethod1Params.UIWDEditText; // Click '百度一下' button Mouse.Click(uISearchButton, new Point(61, 18)); } public virtual RecordedMethod1Params RecordedMethod1Params { get { if ((this.mRecordedMethod1Params == null)) { this.mRecordedMethod1Params = new RecordedMethod1Params(); } return this.mRecordedMethod1Params; } } private RecordedMethod1Params mRecordedMethod1Params; } /// <summary> /// Parameters to be passed into 'RecordedMethod1' /// </summary> [GeneratedCode("Coded UITest Builder", "12.0.21005.1")] public class RecordedMethod1Params { #region Fields /// <summary> /// Go to web page 'about:Tabs' using new browser instance /// </summary> public string UINewtabInternetExplorWindowUrl = "about:Tabs"; /// <summary> /// Type 'www.baidu{Enter}' in text box /// </summary> public string UIItemEditSendKeys = "www.baidu{Enter}"; /// <summary> /// Type 'jaxu cnblogs' in 'wd' text box /// </summary> public string UIWDEditText = "jaxu cnblogs"; #endregion } }
大部分代碼是由Coded UI Test Builder自動生成的,我們只編寫了TestSearchResult()方法,用來尋找控件並獲取到其中的值來進行判斷。測試結果的判斷通過Assert斷言來完成,Assert提供了多種方法以幫助我們實現不同的判斷,具體的內容可以參考msdn。然后對RecordedMethod1()方法做了適當修改。TestSearchResult()方法中對於如何查找和遍歷UI控件在稍后的章節中會討論到。然后我們將所有代碼的調用放到CodedUITest1.cs文件中執行。
[TestMethod] public void CodedUITestMethod1() { UIMap1 uimap = new UIMap1(); uimap.RecordedMethod1(); uimap.TestSearchResult(); }
現在可以通過Test Explorer窗口或者直接使用測試方法的上下文菜單運行或調試該測試方法。如果通過測試,測試方法前面會顯示綠色的圖標,否則會顯示紅色的叉。Visual Studio會為每次測試生成對應的測試報告,在工程目錄下的TestResults文件夾中可以找到所有的測試報告。
有關Assert斷言
在自動化測試中,Assert斷言一旦遇到測試失敗的情況就會拋出異常,從而導致接下來的測試方法或任務不會繼續執行。也就是說,如果一個測試工程中包含了諸多測試方法,經常的情況是一個測試工程中會包含很多個測試類,每個類針對不同的測試用例,而每個測試類中又包含了很多個不同的測試方法。面對如此龐大的一個測試工程,通常會花上數十分鍾甚至數小時才能將預定好的所有測試方法跑完,我們當然不希望看到由於某一個測試方法失敗而導致剩下的所有測試方法均不能得到執行。在自動化測試中,測試方法測試失敗的情況是很普遍的,成功或失敗都是一種結果,這總比程序運行到一半拋出異常要好得多。
然而,Assert斷言總會在測試失敗的時候拋出異常,從而終止程序運行。如下面的測試方法,如果前兩個斷言中有任何一個失敗的話,則剩下的斷言不會被執行。
[TestMethod] public void CheckVariousSumResults() { Assert.AreEqual(3, this.Sum(1001, 1, 2)); Assert.AreEqual(3, this.Sum(1, 1001, 2)); Assert.AreEqual(3, this.Sum(1, 2, 1001)); }
一個有效的解決辦法是將每一個斷言分別放到不同的測試方法中,如下面的代碼:
[TestMethod] public void Sum_1001AsFirstParam_Returns3() { Assert.AreEqual(3, this.Sum(1001, 1, 2)); } [TestMethod] public void Sum_1001AsMiddleParam_Returns3() { Assert.AreEqual(3, this.Sum(1, 1001, 2)); } [TestMethod] public void Sum_1001AsThirdParam_Returns3() { Assert.AreEqual(3, this.Sum(1, 2, 1001)); }
然而在大多數情況下這可能行不通。例如你需要測試一個包含100行的table,對每一行的title列進行text測試,在這種情況下你根本無法為每一個斷言編寫不同的測試方法。首先你無法確定測試方法的數量,其次過多的測試方法會增加維護成本。
另一種我聽到過的解決方法是使用參數化測試,然而據我所知,Coded UI Test中好像並不支持。在其它測試環境中或許有更好的解決辦法。
或許可以使用try-catch語句來截獲Assert斷言所拋出的異常,使程序能夠繼續運行下去。然后我們將所有截獲到的異常信息輸出到自定義的文件中,即自定義測試報告!測試報告可以是任意類型的文檔,記事本或HTML比較常用。既然可以使用try-catch來截獲Assert斷言的異常欣喜,那么我們會很自然地想到使用下面的方法:
[TestMethod] public void CheckVariousSumResults() { MultiAssert.Aggregate( () => Assert.AreEqual(3, this.Sum(1001, 1, 2)), () => Assert.AreEqual(3, this.Sum(1, 1001, 2)), () => Assert.AreEqual(3, this.Sum(1, 2, 1001))); } public static class MultiAssert { public static void Aggregate(params Action[] actions) { var exceptions = new List<AssertFailedException>(); foreach (var action in actions) { try { action(); } catch (AssertFailedException ex) { exceptions.Add(ex); } } var assertionTexts = exceptions.Select(assertFailedException => assertFailedException.Message); if (0 != assertionTexts.Count()) { throw new AssertFailedException( assertionTexts.Aggregate( (aggregatedMessage, next) => aggregatedMessage + Environment.NewLine + next)); } } }
上面的代碼可以很有效地解決問題,但仍會存在問題。MultiAssert.Aggreate()方法中過多的斷言最終會將所有的異常信息拋出,這會大大降低異常信息的可讀性,不太利於我們從測試測試報告中分析出錯的原因。要知道,測試方法最終的目的不是要讓測試程序運行通過,而是通過測試報告來分析被測試對象可能具有的問題。
下面是一個例子,可以用來有效地解決上面提出的問題。
public static class AssertWrapper { public static string AreEqual<T>(T expected, T actual, string message) { string result = null; try { Assert.AreEqual(expected, actual, message); TestLog.WritePass(message); } catch (AssertFailedException ex) { result = ex.Message; TestLog.WriteError(message); } catch (Exception ex) { result = ex.Message; TestLog.WriteError(message); } return result; } public static string AreEqual(string expected, string actual, string message) { string result = null; try { Assert.AreEqual(expected, actual, message); TestLog.WritePass(message); } catch (AssertFailedException ex) { result = ex.Message; TestLog.WriteError(result); } catch (Exception ex) { result = ex.Message; TestLog.WriteError(result); } return result; } public static string AreEqual(string expected, string actual, bool ignorecase, string message) { string result = null; try { Assert.AreEqual(expected, actual, ignorecase, message); TestLog.WritePass(message); } catch (AssertFailedException ex) { result = ex.Message; TestLog.WriteError(result); } catch (Exception ex) { result = ex.Message; TestLog.WriteError(result); } return result; } public static string Fail(string message) { string result = null; try { Assert.Fail(message); } catch (AssertFailedException ex) { result = ex.Message; TestLog.WriteError(result); } return result; } }
AssertWrapper類中的方法可以有多個重載,以滿足不同的需要,其基本思想就是使用try-catch語句來截獲Assert斷言所拋出的異常。TestLog類中的方法負責寫測試報告,你可以將測試報告定義成任何形式。然后定義一個TestSettings類用來收集測試工程中所有的測試斷言。
public class TestSettings { public static void AddResult(List<string> resultList, string result) { if (result != null) { if (resultList == null) { resultList = new List<string>(); } resultList.Add(result); } } }
在每一個.uitest文件的類中,這樣使用上面的方法:
public List<string> faillist; public void ValidateHeader() { TestSettings.AddResult(faillist,AssertWrapper.AreEqual(true, uIHeader.Exists, "test page: Validate Page header text")); }
然后,在所有的測試方法中添加下面的代碼(faillist為泛型List對象,被定義為TestMethod所在的類的私有變量,同時我們通過faillist.AddRange(testPage.faillist)語句將測試頁面類中的泛型List內容添加過來):
if (faillist != null && faillist.Count > 0) { StringBuilder fail = new StringBuilder(); foreach (string s in faillist) { fail.AppendLine(s); } Assert.Fail(fail.ToString()); }
這樣,可以對該測試方法中包含的所有Assert斷言進行統一管理。這樣做有幾個好處:
- 保證程序能夠正常運行
- 對每一個測試方法而言,其中會包含多個驗證方法,而每一個驗證方法中又會包含有多個Assert斷言,使用上述方法可以有效管理所有的斷言。
- 自定義測試報告的格式
- 可以定義測試方法是通過或者失敗。失敗的測試方法通過Assert.Fail()方法來拋出異常,如果一個測試方法中沒有異常拋出,則會被默認為測試成功,盡管事實上測試是失敗的。
Coded UI Test如何搜索一個控件?
在Coded UI Test中,最常見的問題是如何找到被測試的控件。只有找到被測試的對象,才能使用斷言來判斷其中的屬性是否滿足預期的值。大多數情況下,我們都會使用Coded UI Test Builder窗口來捕獲UI上的控件,但有些情況下我們不得不自行搜索需要的控件。一個簡單的例子,在列表控件中如何查找第一個子元素中所包含的文本。就像本文一開始給出的測試需求。如果你通過Coded UI Test Builder直接查找第一個子元素,其中生成的搜索條件往往具有特定性,當頁面的條件發生變化,特定的搜索條件不一定能找到對應的控件。
查看.Designer.cs文件中自動生成的代碼,所有控件的定義都會包含類似於下面代碼的搜索條件:
HtmlEdit mUIEmailEdit = new HtmlEdit(someAncestorControl); mUIEmailEdit.SearchProperties[HtmlEdit.PropertyNames.Id] = "email"; mUIEmailEdit.SearchProperties[HtmlEdit.PropertyNames.Name] = "email"; mUIEmailEdit.FilterProperties[HtmlEdit.PropertyNames.LabeledBy] = null; mUIEmailEdit.FilterProperties[HtmlEdit.PropertyNames.Type] = "SINGLELINE"; mUIEmailEdit.FilterProperties[HtmlEdit.PropertyNames.Title] = null; mUIEmailEdit.FilterProperties[HtmlEdit.PropertyNames.Class] = null; mUIEmailEdit.FilterProperties[HtmlEdit.PropertyNames.ControlDefinition] = "id=email size=25 name=email"; mUIEmailEdit.FilterProperties[HtmlEdit.PropertyNames.TagInstance] = "7"; mUIEmailEdit.Find();
Coded UI Test會試圖通過所有已知的條件來搜索指定的控件,它使用廣度優先查找方法(Breadth-First)。所有SearchProperties可以被視為使用AND條件進行查找,如果通過SearhProperties找到一個或未找到對應的控件,則所有的FilterProperties條件不會被使用。如果通過所有的SearchProperties條件找到多個對應的控件,則嘗試逐個使用給出的FilterProperties條件進行按序匹配(Ordered match),直到找到匹配的控件。如果通過以上給出的所有條件最終找到多余一個的匹配項,則第一個匹配的元素即為找到的控件。
在上面給出的例子中,會按照如下順利進行搜索:
- 搜索會從someAncestorControl控件開始,因為它被作為要搜索控件的祖先。注意祖先並不一定是父節點,它可以是被搜索控件的任意上級控件。
- 每一次遍歷搜索時,會使用所有已定義的SearchProperties條件進行篩選,假設有三個控件均滿足搜索條件,如X1,X2和X3,此時會繼續使用剩下的FilterProperties條件進行篩選。
- 對控件X1,X2和X3分別使用已定義的FilterProperties條件進行篩選,直到匹配到唯一的控件,或者所有的FilterProperties條件均比較完畢。
下面的流程圖說明了這一過程:
有一點需要注意:
- 對Web controls進行搜索:SearchProperties和FilterProperties均支持
- 對WinForms和WPF controls進行搜索:僅SearchProperties支持
在Web controls中,搜索條件的使用可能會涉及到瀏覽器兼容性問題。如篩選條件最終需要通過InnerText來確定控件,而該屬性在某些瀏覽器上並不支持,此時可能引發異常。在程序編碼過程中嘗試給特定的控件指定ID屬性可以更好的解決這一問題,這就需要與程序開發人員進行有效的溝通。從這一點也可以看出,測試驅動開發的重要性。
不要嘗試通過GetChildren()方法來遍歷所有的控件,因為該方法返回結果會很慢,尤其是當頁面中存在大量控件時。可以使用臨近的祖先節點對該控件進行定義(構造函數的參數可以用來指定被搜索控件的祖先),然后通過給定SearchProperties或FilterProperties來對控件進行篩選,然后使用FindMatchingControls()方法來確定要搜索的控件。如下面的代碼用來遍歷Table元素從而找到表中所有的<th/>和<td/>:
HtmlTable uITable = this.UIRelWindow.UIRelDocument.UITable; HtmlRow rowall = new HtmlRow(uITable); UITestControlCollection rows = rowall.FindMatchingControls(); int rowCount = rows.Count; for (int i = 0; i < rowCount; i++) { HtmlHeaderCell allTH = new HtmlHeaderCell(rows[i]); HtmlCell allTD = new HtmlCell(rows[i]); UITestControlCollection THs = allTH.FindMatchingControls(); UITestControlCollection TDs = allTD.FindMatchingControls(); ... ... }
代碼結構調整
.uitest文件針對的是每一個測試頁面,每個頁面都有單獨的驗證方法用來測試頁面上各個不同的部分,具有良好結構的代碼可以使整個測試工程看起來思路清晰。如果有必要,你完全可以使用設計模式來更加簡練地組織工程中的測試方法和類。一個完好的測試工程代碼結構看起來像這樣:
public class TestRunner { public TestRunner() { homePage = new UI.HomePageClasses.HomePage(); } #region Home page actions and validate method private UI.HomePageClasses.HomePage homePage; public UI.PageClasses.HomePage HomePage { get { if ((this.homePage == null)) { this.homePage = new UI.PageClasses.HomePage(); } return this.homePage; } set { homePage = value; } } public void LaunchHomePage() { HomePage.LaunchHomePage(new System.Uri(TestSettings.GetCurrentSiteURL())); } public void ValidateHomePageText() { HomePage.ValidateHomePageText(); } }
使用TestRunner類將工程中所有的驗證方法和UI Actions方法進行包裝,然后在測試方法中進行調用。
[TestMethod] public void IncomeStatementsTest() { testrunner.NavigateToTestPage(); testrunner.ValidateSomething(); } [TestInitialize()] public void MyTestInitialize() { testrunner = new TestRunner(); testrunner.LaunchHomePage(); } ////Use TestCleanup to run code after each test has run [TestCleanup()] public void MyTestCleanup() { testrunner = null; } private TestRunner testrunner;
忘記說明一點,帶有[CodedUITest]特征屬性的類中,我們可以借用MyTestInitialize()方法和MyTestCleanup()方法進行一些初始化操作和清理工作。不要在該類的構造函數中添加任何代碼,通過帶有[TestInitialize]特征屬性的方法進行初始化工作。同樣,帶有[TestCleanup]特征屬性的方法可以用來進行一些清理工作。
另外,和大多數工程一樣,Coded UI Test工程允許使用App.config文件。在工程中添加該文件並加入<appSettings></appSettings>節點以設置配置信息。
<configuration> <appSettings> <add key ="" value=""/> </appSettings> </configuration>
如何使用命令行方式運行測試方法?
除了在Visual Studio中運行測試方法外,我們還可以通過其它許多方式來運行測試方法。使用測試代理和測試控制器可以對所有的測試方法進行有效管理,並可以將測試方法分發到不同的測試機上單獨進行測試,但需要在服務器上進行部署,MSDN上有相應的介紹,這里主要介紹如何通過命令行方式來運行測試方法。
- 編譯Coded UI Test工程,將生成的.dll文件復制到指定的目錄下。
- 打開Windows命令行窗口,轉到目錄c:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE
或者直接打開Developer Command Prompt for VS2012 - 使用如下命令行執行測試方法
MSTest /testcontainer:CodedUITestProject2.dll /test:CodedUITest1.CodedUITestMethod1
msdn上有對MSTest.exe命令行所有參數的說明。有幾點需要說明一下:
- /testcontainer中有必要指定.dll文件的路徑名稱
- /test用來指定.dll文件中所包含的測試方法的全名稱。注意這里的全名稱包含了測試方法所在的類名以及方法名,類必須包含[CodedUITest]特征屬性,測試方法必須包含[TestMethod]特征屬性。
如果你想分發你的測試工程在其它機器上運行,可以編寫.bat文件並將Coded UI Test工程生成的.dll文件放到同一文件夾下。.bat文件的內容看起來像下面這樣:
@echo off @set PATH=c:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE;%PATH% echo ****** This program will start a Coded UI Test Method ****** pause MSTest /testcontainer:CodedUITest1.dll /test:CodedUITest1.CodedUITestMethod1 echo ****** End Coded UI Test Method ******** pause