如果你正在HTTP上使用安全套接字层(SSL)来加密用户数据并且想通过编程来测试你的Web应用你会发现此技术并非广为人知在本月的栏目中我将示范如何建立一个 SSL 测试服务器然后编写测试自动化代码并通过一个简单而又具有代表性的 Web 应用来验证
Microsoft NET环境提供了强有力的测试工具来测试采用SSL安全机制的ASPNET Web应用程序为了说明它们的使用方法我将建立一个SSL服务器并通过一个短小精悍的程序来示范如何自动测试基于HTTPS的Web例子应用程序虽然 在这方面已有个别技术非常经典并且有很好的文章介绍但在与我的许多同事交流中我发现对测试 SSL Web 应用的整体过程的把握相对来说并不是很到位用两幅屏幕截图最能说明问题Figure 展示了一个简单但是很有代表性的ASPNET的Web应用
Figure 一个 ASPNET Web 应用注意我使用的是 SSL 连接因为我要在 Internet 上传送敏感的信用卡信息(注意是https://协议并且在状态栏有一个小锁 图标)
现在让我们想象一下用手工方式是如何测试这个应用程序的我们必须在Web页上输入成百甚至上千的用户名数量以及信用卡号码检查每一个确认码针对预期的结果检查每行代码以确定结果是否正确然后将这些结果记录在一些表格中比如 Excel 电子表格或者文本文件中整个过程耗时低效繁琐并且容易出错
一个更好的方法是利用NET框架的强大能力编写自动化测试例程在程序中用 SSL 发送测试数据然后针对预期的确认码来检查响应流Figure 是一个控制台应用程序它演示了上述的思路
正像你看到的自动化测试案例的基本做法与 Figure 中所示的手动测试是一样的用户名称是Smith物品数量是信用卡号是 通过基于 SSL 的 HTTP 加密后被提交到Web应用测试程序获取 HTTP 响应流并搜索响应流中的CEDDAB这时在该响应流中找到期望的确认码所以测试 自动化程序记录下PASS结果在本栏目后面的三个章节中我将讲解产生如 Figure 所示输出的测试程序演示如何建立一个接受 SSL 请求的测试服务器并讨论如何扩展本文呈现的技术来满足你自己的需要
在我讲解如何编写测试自动化程序之前让我们首先快速回顾一下本文的例子Web应用程序正如你在 Figure 看到的一样有三个 TextBox 控件我使用 Visual Studio NET 缺省的IDTextBoxTextBoxTextBox 来命名它们它们分别对应着用户名称物品数量以及信用卡帐号 Label 控件 用于显示应用程序信息当我编写测试自动化程序时我必须要知道这些信息此外还需要知道订单确认码的产生方式以便我能确定我的测试案例期望的结果以下是用于测试此Web应用程序的核心代码
if (TextBoxTextLength == )
LabelText = Please enter credit card number;
else
{
byte[] input = EncodingUnicodeGetBytes(TextBoxText);
byte[] hashed;
using(MD m = new MDCryptoServiceProvider())
{
hashed = mComputeHash(input);
}
LabelText = Thank you Your confirmation code is +
BitConverterToString(hashed)Substring();
}
为了模拟确认码的生成我只利用了用户输入的信用卡号用它产生一个MD散列然后截取散列值最左边的个字符在实际的生产系统中你可能会用更为复杂的方式来产生确认码在这种情况下确定预期的结果可能会更具技巧性不过有一点要特别注意你不能通过调用被测试的程序来确定预期结果因为这将破坏测试的有效性因为你本来就是要检查 测试自动化程序返回的结果和被测程序返回的结果是否一致
测试自动化程序
这个测试自动化程序出奇的短小其全部代码如 Figure 所示尽管通过编程将数据提交给某个ASPNET Web 应用程序的技术在 MSDN 库中已有文档描述但是其中有几个技巧需要特别关注
我决定编写一个C#控制台程序作为我的测试程序使用和被测程序一样的语言开发测试自动化程序通常是个好主意不管怎么说规划良好的设计并与NET环境集成意味着你能安全地使用Visual Basic NET或其它任何与NET兼容的语言一般来说控制台程序类型最适合作为 测试自动化程序虽然测试程序具备漂亮的用户界面能给用户留下深刻印象但自动化测试程序是一个工具而不是个人秀此外控制台程序也比GUI程序更容易集成到构建系统中
测试自动化程序的整个结构相当简单我将测试案例的数据保存在一个简单的文本文件中每一行数据表示单个测试案例以下是该测试案例文件的内容
:Smith:: :CEDDAB
:Baker:: :CECF
:Gates:: :DA
信息之间使用冒号(:)进行分隔我也可以使用任何字符作为分隔符但在实际的测试案例中避免出现含义模糊的字符很重要第一个字段是测试案例编号第二个字 是用户名称第三个字段是数量第四个字段是信用卡号码第五个字段是预期的确认码如果你不想使用文本文件那么XML文件或 SQL 表 都是很好的可选方案
我的测试自动化程序的基本结构与我的测试案例数据文件是相关在一起的使用伪代码表示如下
loop
read a test case line
parse out test case data
build up data to post to application
convert post data to a byte array
post the data
retrieve the response stream
if response stream contains expected confirmation code
log pass result
else
log fail result
end loop
我首先声明要用到的命名空间这样可以避免用到每个NET类和对象时都得写全称限定名同时测试自动化程序将要涉及哪些类库功能也一目了然 using System;
using SystemWeb;
using SystemText;
using SystemNet;
using SystemIO;
SystemWeb 命名空间包含了 HttpUtility 类这个类可以将一些特殊字符转换为转义字符序列因为缺省的控制台程序并不引用它的所在程序集即 SystemWebdll我们必须手动地添加对它的引用SystemText 命名空间包含了一个Encoding 类我要用它来处理字节数组 (Byte Array)SystemNet 命名空间包含了 HttpWebRequest类 它是将数据提交到 ASPNET Web 应用 的基础类使用 SystemIO 命名空间 是因为我要用数据流处理基于 SSL 的 HTTP 的响应此外我还需要用它从文本文件中读取测试案例数据注意using 指令字 允许你在使用某个命名空间中的类型时不必用长长的限定名
接下来在命令外壳中显示一段简单的启动信息后声明测试自动化 程序要用到的一些关键变量 string url = x;
string viewstate = HttpUtilityUrlEncode(
dDwMDIxOTUwNDQOzE/ailqxXzCUfpbWTPybfSMA==);
string line;
string[] tokens;
StringBuilder data = new StringBuidler();
byte[] buffer;
string proxy = null;
上面大多数变量的目的从其命名一目了然只有 viewstate 是个新变量所以我会对之作简要解释现在我打开测试案例文件并且一行一行地读取
using(FileStream fs = new FileStream(args[] FileModeOpen))
{
StreamReader tc = new StreamReader(fs);
while ((line = tcReadLine()) != null)
{
// parse line post data get response
// determine pass or fail log result
}
}
虽然有很多可选方法来设计此自动化过程但是 上述这个简单的结构已经在几个大型项目中被证明是健壮的下一步是解析测试案例中数据的每个字段并且构建一个包含名称值对 的字符串
tokens = lineSplit(:);
dataLength = ;
dataAppend(TextBox= + tokens[]); // Last name
dataAppend(&TextBox= + tokens[]); // Quantity
dataAppend(&TextBox= + tokens[]); // Credit card number
dataAppend(&Button=clicked);
dataAppend(&__VIEWSTATE= + viewstate);
我使用StringSplit方法将测试 案例数据行分开并且将每个字段保存到tokens数组中 测试案例的ID保存到tokens[]中用户名称保存到tokens[]中物品数量保存到tokens[]中信用卡号保存到tokens[]中为了清晰起见也可以将这些数值复制到额外的 具有描述性的字符串变量中如caseIDlastName等如下所示
caseID = tokens[];
lastName = tokens[];
// etc
但是我想让所使用的变量数为最少传统的Web服务器 一般都用名称值对来 提交(POST)数据多个数据之间用&符号分开如下
lastName=Smith&quantity=&creditCardNo=
但是ASPNET扩展了这种做法在这个例子中 有五个名称值对第一对你可能希望是TextBox=tokens[]它将当前测试 案例的用户名称(保存在tokens[