测试运行
使用 IronPython 进行 .NET 模块测试
James McCaffrey
内容
在测试模块
广告特别 Interactive 模块测试
轻量模块测试自动化
向上覆盖
Python 脚本语言具有功能,使其执行几种类型的软件测试的极好选择。有用包括 CPython 的多个实现 Python,运行类似于 UNIX 的操作系统和 IronPython,实现的计算机上最常见的实现后期 2006 发布目标 Microsoft.NET Framework。在本月的专栏中我将演示了如何使用 IronPython 测试基于.NET 的模块从两个 Python 命令行和轻量 Python 脚本中。假设您有一种脚本语言,如 JavaScript、 Windows PowerShell、 VBScript、 Perl、 PHP,一些经验,或者拼音、,但是我做不假定您有任何经验 Python。请看一下 图 1 以了解我要向。我使用 IronPython 以执行比较名为 TwoCardPokerLib 在.NET 模块中的两个卡手的方法的快速、 特别测试。我描述 图 1 所示此列,后面将详细的命令,但现在观察我以交互方式添加对 DLL 驻留我.NET 模块的引用,实例化从的模块的两个 Hand 对象和调用来确定一只手被难倒其他一个现有的比较方法。现在快速一下 图 2 。该屏幕截图显示一个轻量的 IronPython 脚本,采用我只分钟才能创建,的执行。再次,通过所有详细信息我以后,去,但可以看到我执行测试通过从文本文件读取测试案例数据的传统模块,实例化从 TwoCardPokerLib 模块的两个现有对象,调用比较方法,然后比较实际结果与预期结果来确定测试用例通过还是失败结果。
图 1 特别测试与命令行上的 IronPython
>>> import sys
>>> sys.path
['C:\\IronPython', 'C:\\IronPython\\Lib']
>>> sys.path.append(r'C:\ModuleTestingWithPython\TwoCardPokerLib\bin\ Debug')
>>> sys.path
['C:\\IronPython', 'C:\\IronPython\\Lib', 'C:\\ModuleTestingWithPython\\ TwoCardPokerLib\\bin\\Debug']
>>>
>>> import clr
>>> dir()
['_', '__builtins__', '__doc__', '__name__', 'clr', 'site', 'sys']
>>>
>>> clr.AddReferenceToFile("TwoCardPokerLib.dll")
>>> from TwoCardPokerLib import *
>>> dir()
['Card', 'Hand', '_', '__builtins__', '__doc__', '__name__', 'clr', 'site', 'sys']
>>>
>>> c1 = Card()
>>> c2 = Card("9d")
>>> print c1,c2
As 9d
>>>
>>> h1 = Hand(c1,c2)
>>> h2 = Hand("Ah","8c")
>>> expected = 1
>>> actual = h1.Compare(h2)
>>>
>>> if actual == expected: print "Pass\n",
... else: print "Fail\n"
...
Pass
>>>
>>> ^Z
C:\IronPython>

图 2 使用的 IronPython 脚本的测试自动化
后面的部分中我将介绍测试 TwoCardPokerLib 该类库,以便您将了解完全进行测试。 然后讨论交互式 IronPython 命令我使用 图 1 中。 接下来,我提供,并解释详细生成输出 图 2 中的短 Python 脚本。 测试工具和测试库的完整源代码位于本专栏附带的下载。
我将简要讨论如何修改和扩展以满足您自己的测试的需要此处提供的想法的封装最。 我相信您会发现测试此处提供的技术的 Python 将是一个很好的补充,软件测试技能集。
在测试模块
现在让我们看一下待测试库。 我决定创建一个.NET 类库测试具有足够功能,以显示您面临当执行实际的模块,在生产环境中测试,但不是使库的详细信息隐藏在测试多复杂问题的问题的类型。 我 imagined 假想的每个队员从四个,标准、 52 卡卡片组接收两个卡的位置的两个卡纸牌游戏。 这必然导致与卡类、 一个现有类和比较方法来确定两个现有对象的哪些是更好的面向对象设计。 我决定每个两个卡现有会被归类为一个直接刷新 (在相同牌套连续卡)、 对 (使用相同的等级的两个卡)、 刷新 (两个在相同牌套卡)、 直接 (两个连续卡) 的而 Ace 高到四个高。 请注意三个的高现有不可能因为三两个是一个直线,并且三个 A 是高的 A。 即使这样一个简单例子就是实现而有趣。 IronPython 可有效地测试.NET 库,无论该实现语言并 IronPython 可以来测试传统的 COM 库。 图 3 中列出我 TwoCardPokerLib 库中卡类将源代码。
图 3 卡类测试库
public class Card
{
private string rank;
private string suit;
public Card() {
this.rank = "A"; // A,2, . . ,9,T,J,Q,K
this.suit = "s"; // c,d,h,s
}
public Card(string s) {
this.rank = s[0].ToString();
this.suit = s[1].ToString();
}
public override string ToString() {
return this.rank + this.suit;
}
public string Rank {
get { return this.rank; }
}
public string Suit {
get { return this.suit; }
}
public static bool Beats(Card c1, Card c2) {
if (c1.rank == "A") {
if (c2.rank != "A") return true;
else return false;
}
else if (c1.rank == "K") {
if (c2.rank == "A" || c2.rank == "K") return false;
else return true;
}
else if (c1.rank == "Q") {
if (c2.rank == "A" || c2.rank == "K" || c2.rank == "Q") return false;
else return true;
}
else if (c1.rank == "J") {
if (c2.rank == "A" || c2.rank == "K" || c2.rank == "Q" || c2.rank == "J")
return false;
else
return true;
}
else if (c1.rank == "T") {
if (c2.rank == "A" || c2.rank == "K" || c2.rank == "Q" ||
c2.rank == "J" || c2.rank == "T")
return false;
else return true;
}
else { // c1.rank is 2, 3, . . 9
int c1Rank = int.Parse(c1.rank);
int c2Rank = int.Parse(c2.rank);
return c1Rank > c2Rank;
}
} // Beats()
public static bool Ties(Card c1, Card c2) {
return (c1.rank == c2.rank);
}
} // class Card
为了使我的短代码和主要想法清除,我已经一些您不会采取如省略所有的错误检查在生产环境中的快捷方式。 我的卡类有意设计用于说明的许多典型的.NET 模块的通用功能。 请注意有是默认卡构造函数,创建一个 A 黑桃王卡对象。 没有一个接受字符串 (如作为 td 若要创建代表的菱形卡的 10 个对象的构造函数。 我实现公开秩和卡对象的花色的 get 属性。 并,我实现静态操作和等同值方法,可以将用于确定一个卡对象被难倒还是将另一个卡对象关联。 我的现有类是太长,因此我将仅介绍类的关键部分显示完整的。 现有该类具有两个成员字段:
public class Hand {
private Card card1;
private Card card2;
// constructors, methods, etc.
}
我做出重要假设 card1 是更高版本的 (或可能是等于) 两个卡对象。 这极大地简化了我 hand.Compare 方法的逻辑。 我手动类有多个构造函数,执行模块测试时必须考虑到的典型情况。 默认现有构造函数创建手的形状具有两个 Ace–of 黑桃王卡:
public Hand() {
this.card1 = new Card();
this.card2 = new Card();
}
我实现两个其他现有的构造函数允许我是传入两个 Card 对象或两个字符串:
public Hand(Card c1, Card c2) {
this.card1 = new Card(c1.Rank + c1.Suit);
this.card2 = new Card(c2.Rank + c2.Suit);
}
public Hand(string s1, string s2) {
this.card1 = new Card(s1);
this.card2 = new Card(s2);
}
图 4 中,我实现四种专用帮助器方法。 正如您将看到私有方法未公开的 IronPython 测试脚本。
图 4 专用方法
private bool IsPair() { return this.card1.Rank == this.card2.Rank; }
private bool IsFlush() { return this.card1.Suit == this.card2.Suit; }
private bool IsStraight() {
if (this.card1.Rank == "A" && this.card2.Rank == "K") return true;
else if (this.card1.Rank == "K" && this.card2.Rank == "Q") return true;
// etc: Q-J, J-T, T-9, 9-8, 8-7, 7-6, 6-5, 5-4, 4-3, 3-2
else if (this.card1.Rank == "A" && this.card2.Rank == "2") return true;
else return false;
}
private bool IsStraightFlush() { return this.IsStraight() &&
this.IsFlush();
}
作为一般经验时执行测试您的模块不明确地测试私有方法,。 其目的是测试使用帮助器的公共方法时,将暴露帮助器方法中的任何错误。 则的 hand.Compare 方法是非常复杂。 我编码专用的操作和将关联的帮助器方法,然后使用这些实现公用比较方法:
public int Compare(Hand h) {
if (this.Beats(h))
return 1;
else if (this.Ties(h))
return 0;
else if (h.Beats(this))
return -1;
else
throw new Exception("Illegal path in Compare()");
}
我使用旧的 C 语言 strcmp(s,t) 函数范例 hand.compare—if"左"的现有参数 (在"this"对象) 被难倒"右"参数 (显式的手动输入参数),比较返回 1。 如果在正确的参数被难倒左侧的参数比较将返回-1。 根据我的规则为两个现有对象是否比较返回 0。 大部分工作都是通过在 图 5 中显示的该私有操作方法。
图 5 </a0>-操作方法
private bool Beats(Hand h) {
if (this.IsStraightFlush()) {
if (!h.IsStraightFlush()) return true;
if (h.IsStraightFlush()) {
if (Card.Beats(this.card1, h.card1)) return true;
else return false;
}
} // this.IsStraightFlush()
else if (this.IsPair())
// code
else if (this.IsFlush())
// code
else if (this.IsStraight())
// code
else
// code for Ace-high down to Four-high
}
return false;
}
操作的代码是有关页长并且可以签出详细信息如果您关注通过检查随附的代码下载。 我特意插入到专用等同值方法的逻辑错误:
else if (this.IsFlush() && h.IsFlush() &&
this.card1.Rank == h.card1.Rank) // error
return true;
如果两个现有对象进行比较两个刷新,然后我检查这两个指针是否为高卡相同的等级。 但是我不检查每个现有的第二个卡我应具有:
else if (this.IsFlush() && h.IsFlush() &&
this.card1.Rank == h.card1.Rank &&
this.card2.Rank == h.card2.Rank) // correct
return true;
此逻辑错误生成测试用例故障 图 2 所示。 我构建我库通过使用 Visual Studio 创建名为 TwoCardPokerLib C:\Module TestingWithPython,在一个类库项目导致在 C:\ModuleTestingWithPython\TwoCardPokerLib\bin\Debug TwoCardPokerLib.dll 文件。
广告特别 Interactive 模块测试
现在让我们请参阅如何您可以检查和测试使用 IronPython 的.NET 库。 特别,让我们来看看每一个 图 1 中屏幕快照所示的命令。 图 1 所示的输出的第一个部分表明我使用 IronPython 1.1.1 版。
IronPython 是可从 CodePlex,Microsoft 赞助商开源项目的在 codeplex.com/IronPython 免费下载。 它需要.NET Framework 2.0,并支持框架的该版本的任何计算机上运行。 我使用 Windows Vista。 您真正不安装 IronPython,而您只是一个单一的压缩的文件下载到您的计算机并提取到任何方便的目录及其内容。 在我的示例我存储所有 IronPython 文件和在 C:\IronPython 的子目录。 我调用 Python 命令行解释器 (ipy.exe) 并指定可选的 X: TabCompletion 参数,以启用选项卡完成。 如果您键入 ipy.exe-h 您将得到所有选项的列表。 IronPython 启动它将执行这样的脚本存在名为 site.py,特殊的启动脚本。 我使用标准的 site.py 文件随 IronPython,没有任何修改。
IronPython 初始化后,我检查当前的 IronPython 系统路径信息:
>>> import sys
>>> sys.path
['C:\\IronPython', 'C:\\IronPython\\Lib']
首先,我发出导入 sys 命令使我都访问特殊的 IronPython sys 模块内部方法。 接下来,我显示查找不在当前目录中的文件时,将检查 IronPython 的路径的列表。 可以将此视为有点类似于 Windows 操作系统路径环境变量的一个本地 IronPython 机制。 因为我在如果我想知道哪些属性和方法是提供给我 sys 模块 TabCompletion 功能使用调用 IronPython,我可能已键入 sys。 并重复按 <tab> 键。 接下来我告诉 IronPython 待测试的.NET 模块的位置:
>>> sys.path.append(r'C:\ModuleTestingWithPython\TwoCardPokerLib\bin\Debug')
>>> sys.path
['C:\\IronPython', 'C:\\IronPython\\Lib', 'C:\\ModuleTestingWithPython\\TwoCardPokerLib\\bin\\Debug']
我 path.Append 方法用于系统模块的向 IronPython 搜索列表中添加一个新的目录。 请注意 r 字符前面追加字符串参数。 Python 中您可以使用单引号或双引号的字符串周围。 但是,与某些语言,不同 Python 将评估转义字符 (如单引号的和双引用字符串内的 \ n。 若要确保字符串被解释严格为文本 (在 Python 术语"原始"字符串) 您可以使用 r 修饰符为我做上面。 追加新的目录之后,我验证我没有进行任何键入错误发出 sys.path 命令。 接下来我准备测试我的 DLL 加载可以加载.NET 模块的第一个启用方法:
>>> import clr
>>> dir()
['_', '__builtins__', '__doc__', '__name__', 'clr', 'site', 'sys']
我发出导入 clr (对于公共语言运行库) 命令,现在我 clr 模块具有几种方法可以加载.NET 程序集的访问。 然后,我使用 Dir() Python 命令来查看哪些模块当前有访问权限。 请注意我当前没有访问到 TwoCardPokerLib 库。 现在我可以使用 clr 模块访问测试我库:
>>> clr.AddReferenceToFile("TwoCardPokerLib.dll")
>>> from TwoCardPokerLib import *
>>> dir()
['Card', 'Hand', '_', '__builtins__', '__doc__', '__name__', 'clr', 'site', 'sys']
我要启用我当前的 IronPython 环境来调用 TwoCardPokerLib DLL 使用 AddReferenceToFile 方法。 Python 是区分大小写的因此如果我必须键入 addreferencetofile,是例如我将有就一个非如的属性 (方法) 错误。
添加对测试我模块引用后,我需要使用导入语句使该模块可用。 我可能有类型导入 TwoCardPokerLib,但我键入从 TwoCardPokerLib 导入 * 相反。 如果使用第一个简单窗体,我将不得不完全限定我模块,是例如 TwoCardPokerLb.hand 而不是仅键入手中的所有内容。 导入命令从-<module>-导入-<classes> 形式允许我在我键入时省略了父模块名称。 请注意我执行 Dir() 命令后,看 TwoCardPokerLib 模块内的卡和现有类现在可用。 现在我可以通过创建两个 Card 对象行使测试我模块:
>>> c1 = Card()
>>> c2 = Card("9d")
>>> print c1,c2
As 9d
我使用默认卡构造函数实例化对象名为 c 1。 撤回前一节中默认卡构造函数创建一个 A 黑桃王对象。 我使用非默认卡构造函数创建对象代表的菱形的 9。 观察我不使用像"New"关键字我像在某些语言实例化对象。 如果我必须使用短"导入 TwoCardPokerLib"语句前面,我会调用构造函数为"c 1 = TwoCardPokerLib.card()"。 我使用固有的 Python 打印语句显示我的两个的 Card 对象。 在后台,IronPython 打印语句实际调用 card.ToString() 方法我在我的类库中实现。 现在,我实例化两个现有对象并调用 hand.Compare 方法:
>>> h1 = Hand(c1,c2)
>>> h2 = Hand("Ah","8c")
>>> expected = 1
>>> actual = h1.Compare(h2)
我将刚创建的两个卡对象传递到现有构造函数来创建在第一个的现有,然后我使用的构造函数字符串参数版本实例化第二个手的形状。 因为 A 9 的手形被难倒 A 8 的手形,我希望返回 1,因此我将该值存储在变量名应为该比较方法。 请注意 Python 是一动态类型化的语言,因此我不声明数据类型。 我调用 hand.Compare 方法并存储结果到名为一个变量实际。 我可以通过键入手动现有类中查看可用的方法。 在命令行,然后按 <tab> 键。 我会查看公共方法,如比较和 ToString,但是我不会查看专用的方法,如操作和等同值。 现在我可以为我特别的交互式测试确定通过还是失败结果:
>>> if actual == expected: print "Pass\n",
... else: print "Fail\n"
...
Pass
>>>
Python If-Then 语法在命令行是有点不寻常的因此我将在下一节中介绍。 但实质上是我检查调用实际的变量中的值等于调用在变量中的值应为。 如果是这样,我会显示"传递"消息 ; 否则打印"fail"。 请注意,我已在每个字符串中包括一个换行符。 IronPython 解释器在中间正在"响应表示我有一个不完整的命令解释器正在等待我完成命令。
轻量模块测试自动化
现在让我们了解如何编写测试.NET 类库的轻量 IronPython 脚本。 图 6 中的脚本生成 图 2 所示的输出。
图 6 文件 harness.py
# harness.py
# test TwoCardPokerLib.dll using data in TestCases.txt
print "\nBegin test run\n"
import sys
print "Adding location of TwoCardPokerLib.dll to sys.path"
sys.path.append(r'C:\ModuleTestingWithPython\TwoCardPokerLib\bin\Debug')
import clr
print "Loading TwoCardPokerLib.dll\n"
clr.AddReferenceToFile("TwoCardPokerLib.dll")
from TwoCardPokerLib import *
print "=================================="
fin = open("TestCases.txt", "r")
for line in fin:
if line.startswith("$"):
continue
(caseID,input,method,expected,comment) = line.split(':')
expected = int(expected)
(left,right) = input.split(',')
h1 = Hand(left[0:2],left[2:4])
h2 = Hand(right[0:2],right[2:4])
print "Case ID = " + caseID
print "Hand1 is " + h1.ToString() + " " + "Hand2 is " + h2.ToString()
print "Method = " + method + "()"
actual = h1.Compare(h2)
print "Expected = " + str(expected) + " " + "Actual = " + str(actual)
if actual == expected:
print "Pass"
else:
print "** FAIL **"
print "=================================="
fin.close()
print "\nEnd test run"
# end script
我开始我的 IronPython 测试工具脚本注释:
# harness.py
# test TwoCardPokerLib.dll using data in TestCases.txt
# 是注释字符的 Python 脚本。 Python 是基于行的因此通过在行尾运行的注释。 Python 还支持使用三重单报价的多行注释。 Python 脚本使用.py 文件扩展名,并可以同时创建使用包括记事本任何文本编辑器。 我的测试工具从名为 TestCases.txt 外部文件中读取测试用例数据:
0001:AcKc,AdAs:Compare:1: "royal flush" > pair Aces
0002:Td9d,Th9h:Compare:0: straight flush diamonds == straight flush hearts
$0003:Ah2c,As9c:Compare:1: straight > Ace high
0004:9h6h,9d7d:Compare:-1: flush 9-6 high < flush 9-7
0005:KcJh,As5d:Compare:-1: King high < Ace high
我具有只是五个的测试用例的此处,但我将在生产方案具有成千上万。 有 7,311,616 (或 524) 法律的说明彻底测试更简单的模块的 impracticality 的 hand.compare 输入。
每行代表单个的测试案例。 每个字段由冒号分隔。 第一个字段是一个测试案例 ID。 第二个字段是包含的信息使用一个逗号分隔的两个现有对象的字符串。 第三个字段指示要测试的方法。 第四个字段是所需值。 第五个字段是可选的是测试案例注释。 通知测试案例 0003 被前面 $ 字符。 我将扫描该字符在我的测试工具并跳过所有这些测试用例。 测试用例 0004 因有意的逻辑错误 (仅对于演示目的) 我放 hand.ties 方法是由测试 hand.Compare 方法调用内的生成失败结果。 在一个文本文件中存储测试用例数据是快速而容易。 我可能也有我测试案例数据直接嵌入到我工具,或存储在一个 XML 文件等的我的情况。 Python 可以轻松地处理任何测试用例存储方案。 接下来,我显示一条消息以命令行解释器:
print "\nBegin test run\n"
因为 Python 字符串可以分隔双报价单或单个的引号,转义符计算在两个报价单样式,选择报价类型实际上是的首选项。 我通常使用双引号除了时我将使用 r 修饰符以使我的字符串文本,在这种情况下我倾向于使用单引号。 下一个我的 Python 测试工具脚本会将测试我的 DLL 的位置添加到 Python 系统路径中:
import sys
print "Adding location of TwoCardPokerLib.dll to sys.path"
sys.path.append(r'C:\ModuleTestingWithPython\TwoCardPokerLib\bin\Debug')
此处我硬编码在测 DLL 的位置。 我可以将命令行参数传递给我的 Python 脚本,并使用内置的 sys.argv 数组访问它们。 例如如果我调用我的脚本为
> ipy.exe harness.py C:\Data MyLib.dll
然后 sys.argv[0] 将包含我的脚本 (harness.py),名称 sys.argv[1] 将保存在第一个参数 (C:\Data),和 sys.argv[2] 将保存第二个参数 (MyLib.dll)。 接下来,我告诉我工具加载测试我模块:
import clr
print "Loading TwoCardPokerLib.dll\n"
clr.AddReferenceToFile("TwoCardPokerLib.dll")
from TwoCardPokerLib import *
加载基于.NET 的库使用 IronPython 的方法有多种。 在 clr.AddReferenceToFile 方法十分简单而有效您 sys.path 包含将,库的位置但 clr 模块还包含包括 clr.AddReference、 clr.AddReferenceByName、 clr.AddReferenceByPartialName 和 clr.AddReferenceToFileAndPath 的替代方法。 此处我使用在 * 加载 TwoCardPokerLib 媒体库中的所有类的通配符但我可以都加载特定的类的命名它们。 Python 有几种方法处理文字文件行的行。 我使用内部文件打开函数,并向其传递我的测试案例文件和 r 参数来指示我正在打开要读取该文件的名称:
print "=================================="
fin = open("TestCases.txt", "r")
for line in fin:
# process line here
其他常见的模式包括执行写操作的 w 和 a 的附加。 模式参数是可选的默认为 r,以便我可能已离开它出。 为好对的引用 Python 语言功能、 语法和内部函数,可以找到在 Docs.Python.org。 打开函数的结果是我分配给变量名为 Fin 文件句柄。 然后我使用了 For 循环来循环访问行的行。 我命名我单行存储变量"行",但我可能已使用任何合法的变量名称。 请注意的 Python 的语法特点: 而不使用开始结束标记,如 {。 .. } 或开始。 .. 结束为大多数语言执行,Python 使用冒号字符结合缩进表示已开始和语句块的结束。 在我主要处理循环,我使用内部 startswith 字符串函数检查如果我的测试案例文件中当前行美元符号字符开头:
if line.startswith("$"):
continue
如果我在找到 $ 我使用 continue 语句来跳过中剩余的语句将循环,和读取下一行从我的测试案例文件。 Python 有循环,并决定控制结构一完成组。 接下来,我分析我的测试案例数据行到其单独的域:
(caseID,input,method,expected,comment) = line.split(':')
expected = int(expected)
我使用称为元组和内部拆分字符串函数的 Python 的一个便利的功能快速分析我的数据。 我可以执行使用传统的数组方法看下面,但是我认为,Python 元组方法是同时更短且更易于理解的任务。
tokens = line.split(':')
caseID = tokens[0]
input = tokens[1]
method = tokens[2]
expected = tokens[3]
comment = tokens[4]
我使用 Int() 函数将显式转换调用从类型为类型 int,以便我可以比较的字符串应为该变量与实际需要。 其他有用的类型转换函数包括 Str()、 float()、 long(),和 bool()。 接下来,我执行第二个的分析操作,以提取两个输入的手:
(left,right) = input.split(',')
h1 = Hand(left[0:2],left[2:4])
h2 = Hand(right[0:2],right[2:4])
我使用拆分的第一个分析之后, 变量输入包含字符串值 (如 KcJh,As5d。 因此,我调用拆分再次使用,参数,并将两个结果存储左名的变量和权限。 现在字符串变量名为 left KcJh 和种右侧包含 As5d。 与许多的语言不同 Python 没有 substring 方法。 相反,Python 使用数组中提取子字符串的索引。 数组从 0 的索引开始,因此如果变量左拥有 KcJh 再向该表达式左 [0: 2] Kc 和表达式中保留 [2:4] 得到 Jh 的产生。 请注意从较大的字符串中提取子字符串,您指定第一个字符的索引在更大的字符串 (如您所料) 但一个大于结束字符的索引。 一旦我有代表单个卡的字符串,我将它们传递到现有的构造函数。 接下来我回显到外壳我输入的数据:
print "Case ID = " + caseID
print "Hand1 is " + h1.ToString() + " " + "Hand2 is " + h2.ToString()
print "Method = " + method + "()"
Python 使用在 + 执行字符串连接,以便我将三个打印语句中上面打印三个字符串的字符。 打印功能可以接受多个参数使用分隔,使我可以编写如下所示以产生相同的输出的语句:
print "Case ID = " , caseID
现在,我可以调用测试方法:
actual = h1.Compare(h2)
print "Expected = " + str(expected) + " " + "Actual = " + str(actual)
注意我实现了 hand.Compare 方法作为实例方法,因为我调用它从 h1 现有对象的上下文。 如果我必须编写为静态方法的比较我会调用它就像这样:
actual = Compare(h1,h2)
请注意到目前为止实际和预期变量是 int 类型的因为实际是从定义为返回 int,并应为比较方法返回值显式转换为 int。 因此我转换实际和预期为字符串,以便我可以连接单个输出字符串。 现在,我显示我的测试案例通过还是失败结果:
if actual == expected:
print "Pass"
else:
print "** FAIL **"
print "=================================="
Python 使用缩进,来指示开头和结尾的块语句。 如果其他语言中有大量体验编程的缩进的 Python 的使用可能看起来有些奇怪一开始。 但大多数测试人员我已经说说 Python 的特殊语法问题熟悉非常快速。
如果我想跟踪的传递的测试用例总数,我可以初始化计数器我主要处理循环外像下面这样:
numPass = numFail = 0
并修改我的 If-Then 结构:
if actual == expected:
numPass += 1
print "Pass"
else:
numFail += 1
print "** FAIL **"
然后可以打印处理循环外的结果:
print "Num pass = " , numPass , " num fail = " , numFail
注意 Python 不支持类似 numPass J++ 语法或 ++ numPass 递增一个 int 变量。 此处我只打印结果命令行解释器但我可以轻松地写入结果文本文件、 XML 文件、 SQL 数据库或其他存储。 现在,我完成我的测试工具设置:
fin.close()
print "\nEnd test run"
# end script
关闭对释放其文件句柄,然后打印一条消息指出测试完成的测试案例的文件引用。 我的脚本结构是非常简单,但 Python 具有允许您管理复杂的脚本的功能。 是例如在生产环境中我将明确封装异常处理程序中的我整个脚本:
try
# harness code here
except
# handle any exceptions here
此外,Python 的支持是程序员定义的函数,因此我可以具有结构我工具如 main 函数以及帮助器函数。
向上覆盖
自动的模块测试) 都是认为最基本类型的软件测试自动化。 我想要评估测试自动化的编程语言的适用性时, 我首先检查所使用的语言处理模块测试程度。 在我看来,IronPython 的 flying colors 通过此 litmus 测试。 没有编程语言都是十全十美为所有测试任务和方案。 也就是说,IronPython 具有,使其作为模块测试语言的极好选择的许多功能。
通过一提的 IronPython 的自动化与单元测试的方式小型模块测试,请允许我完成。 与如 Nunit 进行测试的框架的单元测试通常位于直接在您的模块代码。 使用单元测试的测试驱动开发是主要开发人员活动。 使用单元测试开发过程中的方法不会不 absolve 您从执行彻底的责任,专用模块测试。 这是其中 Python 有。 换句话说,测试 Python 的模块是一个不能代替,单元测试补充。 两种方法一起使用时您可以生成更好、 更可靠的软件。
将您的问题和意见发送 James 到 testrun@Microsoft.com.
JamesMcCaffrey 博士 适用于 Volt Information Sciences,Inc.,他管理在 Microsoft 工作的软件工程师的技术培训。 他有效多种 Microsoft 产品包括 Internet Explorer 和 MSN Search。 James 是 .NET Test Automation Recipes 的作者。 在可以访问他 jmccaffrey@volt.com 或 v-jammc@Microsoft.com.