目錄
待測試系統
測試工具
其他注意事項
無論 您是剛剛接觸 Windows® Communication Foundation (WCF) 還是有過一點 使用經驗,都可以通過一些測試技巧和原理輕松掌握 WCF。有多種方法可以弄清 WCF 究竟是什麼,我更傾向於將 WCF 服務看作是 Web 服務的重要擴展。與 Web 服務一樣,WCF 服務允許您使用面向服務的體系結構來創建分布式系統。但是, WCF 服務提供了更大的靈活性(如選擇傳輸協議)和附加功能(如事務和安全性 )。WCF 絕非僅僅是 Web 服務的擴展,但如果您剛剛接觸 WCF,開始時這樣看 待 WCF 服務也未嘗不可。
圖 1 是一個簡單但極具代表性的 WCF 方案。 在這裡,Internet Explorer® 充當客戶端程序並訪問 ASP.NET Web 應用程 序,用來接受來自用戶的一些文本並計算其加密哈希值。ASP.NET Web 應用程序 在後台調用 WCF 服務來實際執行哈希運算。在這個特定方案中,WCF 服務由 IIS 承載並被 ASP.NET Web 應用程序使用,但正如我要在稍後進行說明的那樣 ,除 IIS 之外,WCF 服務還可以通過多種方式承載,並且實際上可被任何類型 的應用程序或其他服務使用。
圖 1 典型的 WCF 應用程序方案
最基本的 WCF 服務測試類型包括驗證服 務操作功能的正確性。一種方法是通過應用程序 UI 手動測試 WCF 服務。盡管 手動測試是必要的,但使用此方法來測試 WCF 服務的基本功能會是一項耗時、 易錯、低效而且乏味的工作。
更好的方法是編寫測試自動化軟件,一個 類似於圖 2 所示的運行程序。此屏幕快照顯示了我編寫的一個控制台應用程序 測試工具,它可以將輸入文本直接提供給後端 WCF 服務,然後從該服務獲取響 應消息並確定測試用例結果(通過或失敗)。圖 3 中的圖表是一個簡化視圖, 它總結了圖 1 和圖 2 中所示程序之間的關系。在許多情況下,WCF 服務都從後 端數據庫檢索信息或者從 Web 服務或 WCF 服務檢索信息,但我並未在圖 3 中 包括這些情況。
圖 2 測試 WCF 服務(
圖 3 簡化關系
接下來,我將對後端 WCF 服務進行說明以便您對所測試 的內容有准確的了解,然後還將簡要討論圖 1 中所示的使用 WCF 服務的 ASP.NET Web 應用程序,此外還會對測試工具做詳細的介紹。最後我會講一講其 他一些 WCF 測試方案。
待測試系統
待測試系統由後端 WCF 服務 和使用該 WCF 服務的 ASP.NET Web 應用程序組成。WCF 服務非常靈活。創建 WCF 服務時的重要設計決策之一是如何為該服務選擇承載機制。主要有四種方法 :使用 IIS、使用 Windows® Service、自承載以及使用 Windows Activation Service (WAS)。您可能很熟悉 IIS 和 Windows Service 的使用。 自承載涉及在 Microsoft® .NET Framework 托管的程序(如控制台應用程 序)內承載 WCF。WAS 是 Windows Server® 2008 和 Windows Vista® 中提供的一種新的進程激活機制。每種 WCF 承載方案都有一些優點和缺點,具 體取決於您的特定開發方案。在本專欄的 WCF 服務示例中,我決定使用 IIS。 此方案能充分利用 IIS 的一些優勢,例如內置集成的管理和監視、進程回收、 空閒關機和基於消息的激活等功能。
創建由 IIS 承載的 WCF 服務極為 簡單。首先我在 Windows Server 2003 上激發 Visual Studio® 2008。請 注意,如果您決定在運行 Windows Server 2008 或 Windows Vista 的計算機上 開發 WCF 服務,在開發期間必須處理與其增強的安全功能有關的一些問題。但 限於篇幅,我無法在此對這些問題進行說明。
虛擬實驗室:使用 LINQ 進行測試
測試 SQL 存儲過程時,LINQ 可以明顯簡化測試自動化程序。 您可以在我們的虛擬實驗室中體驗這些 LINQ 測試方法。包括本專欄所介紹的項 目和預配置的 SQL Server® 數據庫在內,一切都已安裝完畢並准備就緒。 只需根據顯示的內容開始實驗和編碼即可。
在虛擬實驗室中進行試驗:
go.microsoft.com/fwlink/?LinkId=120461
接下來,我從 Visual Studio 菜單中選擇“文件”|“新建”|“ 網站”選項。 然後從“新建網站”對話框中選擇“WCF 服務”模板(在 Visual Studio 2008 中是默認安裝的)並指向 .NET Framework 3.5。在“位置”字段中,選擇 HTTP 並指定 localhost/WCF/CryptoHashService。此方法會在我開發計算機中的C:\Inetpub\wwwroot\WCF\CryptoHashService目錄下創建一個完整的 Web 應用程序 和 IIS 虛擬目錄;但也可以在計算機的文件系統中事先選擇一個位置並使用內 置的 Visual Studio Web Development Server。
我決定使用 C# 作為我 的實現語言;但 WCF 服務也可以使用 Visual Basic® .NET 來實現。單擊 對話框中的“確定”按鈕後,Visual Studio 會使用兩個名為 GetData 和 GetDataUsingDataContract 的示例操作創建一個全功能的 WCF 服 務。如果您看一下“解決方案資源管理器”窗口,您就會發現 Visual Studio 生成了四個密鑰文件:IService.cs、Service.cs、Service.svc 和 web.config。文件 IService.cs 擁有 WCF 操作的接口定義,文件 Service.cs 擁有操作的實際實現。在本例中,我決定將這兩個文件分別重命名 為 ICryptoHashService.cs 和 CryptoHashService.cs。接著,我將 ICryptoHashService.cs 加載到 Visual Studio 代碼編輯器中,並刪除示例接 口代碼而將其替換為以下代碼:
[ServiceContract]
public interface ICryptoHashService
{
[OperationContract]
string GetCryptoHash(string s);
}
此處我只有一個 單一操作 GetCryptoHash,但我也可以添加其他操作。請注意, [SeviceContract] 和 [OperationContract] 屬性將在後台執行大部分實際的代 碼生成工作。接著,通過添加一個引用 System.Security.Cryptography 命名空 間的 using 語句來編輯實現文件 CryptoHashService.cs,我編寫了下列代碼:
public class CryptoHashService : ICryptoHashService
{
public string GetCryptoHash(string s)
{
byte[] ba = Encoding.Unicode.GetBytes(s);
MD5CryptoServiceProvider sp = new MD5CryptoServiceProvider();
byte[] hash = sp.ComputeHash(ba);
string result = BitConverter.ToString(hash);
return result;
}
}
首先,我更改了 CryptoHashService 類以及從中派生而來的 類的名稱,以匹配我在接口定義中使用的名稱。在 GetCryptoHash 方法中,我 只是使用 GetBytes 方法將輸入參數轉換為一個字節數組,然後實例化 MD5CryptoServiceProvider 類的實例,最後使用 ComputeHash 方法將我的字節 數組轉換為 16 子節 MD5 加密哈希值。我使用靜態 BitConverter.ToString 方 法將得到的哈希值從字節數組轉換為能夠被用戶理解的字符串,然後返回該字符 串。為簡化示例,我省略了在生產環境中必不可少的常規錯誤檢查,例如,檢查 輸入參數、捕獲異常等。接下來,我編輯了 Service.svc 文件以反映從 "Service" 到 "CryptoHashService" 的命名更改:
<%@ ServiceHost Language="C#" Debug="true" Service="CryptoHashService"
CodeBehind= "~/App_Code/CryptoHashService.cs" %>
通過在 web.config 中更新兩個名稱引用來完成名稱編輯:
<system.serviceModel>
<services>
<service name="CryptoHashService" behaviorConfiguration="Servic Behavior">
<!-- Service Endpoints -->
<endpoint address=""binding="wsHttpBinding" contract="ICryptoHashService">
請注意條目 binding="wsHttpBinding"。WCF 綁定是指定 WCF 服務如何與客戶端 進行通信的信息集合,這裡所說的通信包括該服務所使用的傳輸協議、使用的文 本編碼方案等。您可以使用內置綁定,也可以創建自定義綁定。wsHttpBinding 是一個預配置的綁定,默認情況下用於創建由 IIS 承載的 WCF 服務。Visual Studio 所生成的 web.config 文件的另一部分是:
<endpoint address="mex" binding="mexHttpBinding"
contract="IMetadataExchange"/>
此條目指示 WCF 服務顯示自身的元數據,以使客戶端程序可以查看該服務以確定如何與其交 互。此時,可以通過從主菜單選擇“生成”|“生成解決方案 ”來成功生成我的 WCF 服務。也可以按快捷鍵 F5 來生成我的 WCF 服務 並獲取有關創建客戶端應用程序的說明。由於我的服務是由 IIS 所承載,因此 不必顯式啟動該服務;只要 IIS 在運行,WCF 服務即可接受傳入的 WCF 消息。
現在讓我們看一下圖 1 中所示的 ASP.NET Web 應用程序的創建過程。 啟動 Visual Studio 的一個新實例並發出“文件”|“新建 ”|“網站”命令。在“新建網站”對話框中,選擇 “ASP.NET 網站”模板並在框架目標下拉控件中選擇 .NET Framework 3.5。在“位置”中選擇 HTTP 並選擇 C# 作為我的語言 。在位置字段中鍵入 localhost/WCF/UtilitiesAndTools 以隱式命名我的 Web 應用程序。也可以在開發計算機中指定文件系統位置,然後使用開發 Web 服務 器而不使用 IIS。接著,在 Web 應用程序中添加最簡單的 UI 代碼。
圖 4 中的代碼是基本的 UI,沒有字體樣式和 <hr/> 標記等格式設置細節。 您可以從本專欄隨附的代碼下載中獲取具體的 UI 代碼以及本文中列出的所有代 碼。
圖 4 ASP.NET Web 應用程序 UI 代碼
<body>
<form id="form1" runat="server">
<div>
<asp:Label runat="server" ID="Label"
Text="Demo Utilities Featuring WCF Services" />
<asp:Label runat="server" ID="Label2"
Text="Enter text here:" />
<asp:TextBox runat="server" ID="TextBox1"
Height="100px" Width="320px" />
<asp:Button runat="server" ID="Button1"
Text="Get MD5 Crypto-Hash" onclick="Button1_Click"
Width="150px" />
<asp:Button runat="server" ID="Button2"
Text="Get SHA1 Crypto-Hash"
Width="150px" />
<asp:Label runat="server" ID="Label3"
Text="Crypto-Hash of your text (computed by WCF Service) is:" />
<asp:TextBox runat="server" ID="TextBox2"
Width="320px" />
</div>
</form>
</body>
此時,我的 ASP.NET Web 應用程序並不了解我 的 WCF 服務;但是,WCF 和 ASP.NET 技術是被設計為可以無縫協作的。在 Web 應用程序項目的“解決方案資源管理器”窗口中,右鍵單擊項目名稱 ,然後從上下文菜單中選擇“添加服務引用”。請注意,這是一個新 選項,它完善了原來的選項“添加引用”(通常用於 DLL 庫和 .NET 命名空間)和“添加 Web 引用”(通常用於 ASP.NET Web 服務)。
在得到的“添加服務引用”對話框中,鍵入 localhost/WCF/CryptoHashService/Service.svc,然後單擊“執行 ”按鈕。“添加服務引用”工具隨後將掃描指定位置尋找可用 服務並顯示找到的服務;在本例中是 CryptoHashService WCF 服務。在對話框 的“命名空間”字段中,接受簡單的、說明性不是很強的默認名稱 ServiceReference1,然後單擊“確定”按鈕。Visual Studio 生成 了應用程序連接到 WCF 服務時所需的所有代理代碼。特別是我得到了一個名為 CryptoHashServiceClient 的類(即,附加到 WCF 服務名稱後的 "Client"),通過它可以與 CryptoHashService 服務進行通信。
在設計視圖中,雙擊 Button1 控件以指示 Visual Studio 注冊該控件 的事件處理程序。然後將以下代碼添加到 Button1_Click 方法中:
try
{
string s = TextBox1.Text;
ServiceReference1.CryptoHashServiceClient c =
new ServiceReference1.CryptoHasahServiceClient();
string h = c.GetCryptoHash(s);
TextBox2.Text = h;
}
catch (Exception ex)
{
TextBox2.Text = ex.Message;
}
簡直是太簡單了。我將文本提取到 TextBox1 中、實例化自動生 成的 CryptoHashServiceClient 類的一個實例、調用對象的 GetCryptoHash 方 法,並在 TextBox2 中顯示得到的 MD5(消息摘要算法 5)加密哈希值。圖 1中 Web 應用程序的用戶界面顯示了一個計算 SHA-1(安全哈希算法 1)加密哈希值 的按鈕控件,但我並沒有實現該功能來模擬開發中的系統。生成 Web 應用程序 後,用戶可以啟動 Internet Explorer 並導航到該應用程序,然後輸入一些文 本,獲取由後端 WCF 服務計算得出的文本 MD5 加密哈希值,如圖 1 所示。
測試工具
現在,讓我們看一下圖 2 所示的簡單測試工具的代碼 。實際上,測試工具程序只是一個 WCF 客戶端,用於將輸入消息發送到待測試 的 WCF 服務並驗證返回消息是否正確。首先啟動 Visual Studio 的一個新實例 並創建一個名為 TestHarness 的 C# 控制台應用程序。在圖 2 所示的示例中, 可注意到我將工具的位置設置為C:\Inetpub\wwwroot下的一個子目錄,以使工具 代碼靠近待測試系統。實際上可以將工具放在任何位置,包括單獨的測試主機上 。圖 5 顯示了測試工具的整體結構。
圖 5 測試工具結構
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
namespace TestHarness
{
class Program
{
static void Main(string[] args)
{
try
{
// display startup messages
// set up test case data collection, testCases
// set up WCF binding object, wsb
// set up WCF address object, epa
CryptoHashServiceClient c =
new CryptoHashServiceClient(wsb, epa);
foreach (TestCaseData tcd in testCases)
{
// echo case ID, input, expected values
// call GetCryptoHash method, fetch return
// compare actual return with expected
// print pass/fail result
}
Console.WriteLine("\nDone");
}
catch (Exception ex)
{
Console.WriteLine("Fatal error: " + ex.Message);
}
}
}
class TestCaseData
{
public readonly string caseID;
public readonly string input;
public readonly string expected;
public TestCaseData(string caseID, string input, string expected)
{
this.caseID = caseID; this.input = input; this.expected =
expected;
}
}
}
首先添加一個指 向 System.ServiceModel 命名空間的 using 語句,對我的測試工具進行編碼。 此命名空間包含核心 WCF 服務功能。接著設置測試用例數據。在圖 5 中,您可 以看到我創建了一個簡單的 TestCaseData 類來保存測試用例 ID、輸入內容和 預期值。在生產環境中,您還可以添加其他字段(包括 WCF 服務綁定信息), 稍後我會進行解釋。現在,通過創建一個通用的 List 對象並用 TestCaseData 對象對其進行填充,來將測試用例數據直接嵌入到我的工具中:
List<TestCaseData> testCases = new List<TestCaseData>();
testCases.Add(new TestCaseData ("001", "Hello world",
"E6-76-0D-55 -5C-32-F6-6F-5E-15-93-31-DB-20-FD-8E"));
testCases.Add(new TestCaseData("002", "Goodbye world",
"1A-05-3C-C0-4A-18-13-06-0E-AC-EA-BA-46-EC-CF-B1"));
testCases.Add(new TestCaseData("003", "",
"D4-1D-8C-D9-8F-00-B2-04-E9-80-09-98-EC-F8-42- 7E"));
測試用例數據也可以在外部存儲(如 XML 文件或 SQL 表)中設置。此處我使用了三個測試用例,但在實際中可以使用數千個。在 軟件測試過程中,最困難的環節是如何鑒別良好的測試用例輸入(徹底測試待測 試系統)並確定預期結果。
現在,盡管您可以從頭編寫 WCF 客戶端代碼 ,但如果使用 Visual Studio 2008 隨附提供的 svcutil.exe 命令行工具來生 成代理代碼和 WCF 配置文件則會簡單很多。在本例中,我啟動了一個 Visual Studio 命令外殼(它知道 svcutil.exe 的位置),然後輸入以下命令:
> svcutil.exe http://localhost/WCF/CryptoHashService/Service.svc
在 WCF 服務的位置運行且沒有任何附加參數的 Svcutil.exe 會從該服務讀取 WCF 元數 據並為我生成兩個文件。第一個文件是 CryptoHashService.cs(目標 WCF 服務 的名稱,後跟 .cs 擴展名),它包含 C# 代理代碼,可以使用此代碼與我的 WCF 服務進行消息傳遞。第二個文件是 output.config,它包含目標 WCF 服務 使用的 WCF 綁定信息(傳輸協議、超時設置、文本編碼等)。這兩個文件生成 完畢後,右鍵單擊測試工具項目並將 CryptoHashService.cs 文件添加到項目中 。也可以直接將此代碼復制或粘貼到我的測試工具中。
現在有兩種方法 可以使用 output.config 文件中的綁定信息。一種方法是將文件重命名為 app.config,然後將其添加到客戶端項目中。第二種方法是檢查 output.config 文件中的數據,然後編寫代碼以編程方式將 output.config 中所示的值分配給 綁定對象。編寫常規 WCF 客戶端程序時,app.config 方法通常是比編程方法更 好的選擇。由於 app.config 文件是普通文本文件,由客戶端程序在運行時讀取 ,因此如果關聯的 WCF 服務的綁定信息發生變化,則只需更改 app.config 文 件便可更改相應的客戶端綁定信息,而無需重新編譯客戶端。但是,在 WCF 測 試工具客戶端的特殊情況下,有時最好以編程方式指定綁定信息。使用編程方法 您可以輕松地在測試用例的輸入過程中傳遞綁定信息。此處我使用編程方法。我 將實例化一個 WSHttpBinding 對象。
WSHttpBinding wsb = new WSHttpBinding();
現在我可以將值分配給綁定對象的 Name 屬性 和各種時間屬性:
wsb.Name = "WSHttpBinding_ICryptoHashService";
wsb.CloseTimeout = TimeSpan.Parse("00:01:00");
wsb.OpenTimeout = TimeSpan.Parse("00:01:00");
wsb.ReceiveTimeout = TimeSpan.Parse("00:10:00");
wsb.SendTimeout = TimeSpan.Parse("00:01:00");
我通過以可視化方式 檢查 output.config 文件(由 svcutil.exe 工具生成)中綁定條目的屬性來確 定這些值。在本例中,我將使用在創建 WCF 服務時 Visual Studio 所生成的所 有默認值。其余的綁定屬性也以類似的方式來指定值:
wsb.BypassProxyOnLocal = false;
wsb.TransactionFlow = false;
wsb.HostNameComparisonMode =
System.ServiceModel.HostNameComparisonMode.StrongWildcard;
wsb.MaxBufferPoolSize = 524288;
wsb.MaxReceivedMessageSize = 65536;
wsb.MessageEncoding =
System.ServiceModel.WSMessageEncoding.Text;
wsb.TextEncoding = System.Text.Encoding.UTF8;
wsb.UseDefaultWebProxy = true;
wsb.AllowCookies = false;
現在設置我的代理對象:
string uri =
"http://vte014.vte.local/WCF/CryptoHashService/Service.svc";
EndpointAddress epa = new EndpointAddress(uri);
CryptoHashServiceClient c =
new CryptoHashServiceClient (wsb, epa);
CryptoHashServiceClient 類在 CryptoHashService.cs 文件的內部定義,此文件是使用我創建的 svcutil.exe 自動生成的。該類構造函數接受綁定對象(我剛剛以編程方式設置的)和指向 WCF 服務的 EndPointAddress 對象。現在我的客戶端已經被實例化,接下來我 可以開始迭代每個測試用例並執行待測試 WCF 服務。
在圖 6 中,對於 每個測試用例,我只是向控制台發送一條通過/失敗消息。在生產環境中,您可 能需要執行更多操作,例如跟蹤通過的用例總數和失敗的用例數、以編程方式發 送電子郵件消息(如果一個或多個測試用例失敗)、將結果保存到外部數據存儲 設備等。如果您使用 Team Foundation Server,則可以通過我在“使用 Team System 自定義測試自動化”中介紹的方法來管理此測試工具,該文 章發表在 2008 年的“測試運行”專欄中(請參閱 msdn.microsoft.com/magazine/cc164248)。
圖 6 顯示各測試用例的狀態
foreach (TestCaseData tcd in testCases)
{
Console.WriteLine("Case ID = " + tcd.caseID);
Console.WriteLine("Input = " + tcd.input);
Console.WriteLine("Expected = " + tcd.expected);
string actual = c.GetCryptoHash(tcd.input);
Console.WriteLine("Actual = " + actual);
if (actual == tcd.expected)
Console.WriteLine("* Pass *");
else
Console.WriteLine("** FAIL **");
}
其他注意事項
本月我所提出的一些方 法可以為初步了解基本的 WCF 服務測試奠定堅實的基礎。但是有關 WCF 測試還 有許多其他方面的內容,我將在以後的專欄中加以介紹。其中的大多數附加測試 主題是通過 WCF 強大的靈活性來實現的。例如,WCF 不但允許系統使用傳輸級 別(例如,使用 HTTPS)的安全性,也允許使用較低級別的安全性。盡管 WCF 服務可以使用 HTTP,但 WCF 也允許系統使用多種其他機制(包括 TCP 和命名 管道)進行通信。正如我在上面介紹的,WCF 服務可以在 IIS 中承載,但 WCF 服務也可以通過其他方式承載(包括通過 Windows 服務和自承載托管的應用程 序)。WCF 服務可以支持多個端點,每個端點都有一個不同的地址、綁定和約定 。WCF 支持請求答復式消息傳送和雙工式消息傳送模式。所有這些 WCF 方案以 及許多其他方案在進行全面測試時都有一些值得關注的暗示。
本專欄中 介紹的基本 WCF 功能測試方案只代表全面 WCF 測試的一部分。由於我的虛擬 WCF 加密哈希服務非常簡單,因此整個邏輯都包含在單個的 GetCryptoHash 方 法中。在一些實際的方案中,您可能需要編寫封裝業務邏輯的代碼和單獨的封裝 服務功能的代碼。此方法允許您分別測試業務邏輯和服務,從而簡化您的測試工 作。
使用 Visual Studio Team System 創建 WCF 服務時,您可以利用 內置的單元測試支持(如果您使用的是測試驅動的開發原理)。您也可以使用 Visual Studio 2008 隨附的 WcfTestClient.exe 測試客戶端實用程序來執行 WCF 服務的手動測試,以實現我在本專欄中提供的自動測試類型(請參見 《MSDN® 雜志》我的同事 Juval Lowy 撰寫的專欄,網址為 msdn.microsoft.com/magazine/cc163289)。除了純粹的功能測試以外,您還可 以使用 Visual Studio 中集成的負載測試工具來執行負載測試。
請將您 想向 James 詢問的問題和提出的意見發送至 [email protected]。
James McCaffrey 博士供職於 Volt Information Sciences, Inc.,在 該公司他主要負責管理面向在 Microsoft 工作的軟件工程師的技術培訓。他參 與過多項 Microsoft 產品的研發工作,其中包括 Internet Explorer 和 MSN 搜索。James 也是《.NET 軟件測試自動化之道》一書的作者。可通過 [email protected] 或 [email protected] 與他聯系。
本文配套源碼