BizUnit Context 教程






4.47/5 (4投票s)
本文将介绍如何使用 BizUnit 框架的关键部分——Context 对象。
引言
BizUnit 是一个高度可扩展的测试框架。Context 对象是 BizUnit 的核心元素之一。不幸的是,文档并未说明用户如何利用它,开发者只能通过 NDoc 文档中的一些代码片段自行理解。
本教程首先简要介绍 BizUnit,然后介绍 Context 对象,并解释如何在基于 BizUnit 的测试中使用它。在适用的情况下,它将使用官方文档中的示例,并在文档信息不甚清晰的地方进行补充。它不会复制方法和属性列表等内容,因此,如需详尽参考,请回溯文档。本文将解释三种主要的上下文加载器步骤,未来的文章将探讨自定义步骤以及 Context 在单元测试和系统测试中的高级用法。
请注意,其中一些文件路径是指向 BizUnit 源代码结构和功能测试(例如,TestData 文件夹是附随源代码的单元测试集中的一个特定文件夹),因此我假设您已获取代码并能跟上。
作者附注:2006年11月23日:根据收到的反馈,我已更新本文,增加了一个关于 BizUnit 基础知识的部分。
2007年3月24日更新:对于尚未接触过该项目的人,我已启动 BizUnitExtensions 开源项目,您可以下载 BizUnit 核心的 bug 修复以及大量附加步骤。
2007年5月5日更新:Kevin 已将 BizUnit 迁移到 CodePlex,并发布了一些包含 bug 修复和增强的新版本。Context 已进行大量增强,并将尽快记录。Extensions 项目将不再在其常规版本中发布 Core,以避免社区中关于应使用哪个源代码的混淆。
BizUnit - 基础知识
BizUnit 是一个为 BizTalk 设计的单元测试工具。它也是一个可扩展的框架,允许开发者编写更多测试来丰富开箱即用的功能。它使用 NUnit。
有关 BizUnit 的介绍,您可以阅读这篇由该工具的作者 Kevin Smith 撰写的文章。您也可以看看 CodeProject 上 Naveen 的文章。
基本上,BizUnit 以黑盒模式工作。假设我们有一个编排,它从文件位置获取数据,然后将其提交到队列或 Web 服务,最后写入数据库。现在,如果我们想手动测试它,我们就必须创建一个示例文件,将其放置在对应于编排“接收位置”的文件夹中,然后检查它是否已到达队列,并最终到达数据库(如果 Web 服务本身记录了 SOAP 请求,我们可以检查相应日志文件中是否存在条目)。
使用 BizUnit,我们可以自动化手动完成的步骤。我们可以创建一个包含所有示例文件的文件夹(这是必要的手动步骤),并编写 BizUnit 步骤来复制文件到接收位置,以及检查队列、日志文件和数据库。
事实证明,由于大多数步骤涉及复制文件、发布到 SOAP 端点以及检查队列、数据库和事件日志,因此该工具也可用于测试非 BizTalk 项目。我们可以简单地忽略与 BizTalk 相关的步骤,例如 BAMQueryStep。
(注意:实际上并没有 LogFileReader
步骤,因为我们都有不同的日志文件格式,但可以编写一个,或者如果我们有固定的格式,我们可以使用 XmlContextLoader
或 RegExLoader
从文件中提取内容并进行检查。)
与所有自动化测试一样,当您需要执行一次或两次此步骤时,手动完成很容易,但当数据条件开始增长,并且您需要将自动化测试作为自动化构建和发布流程的一部分时,它就变得无价。此外,在进行集成测试并需要验证消息在各种服务和组件之间的流转时,手动测试根本无法持续。
它的问题在于,对于大型测试脚本,BizUnit 脚本很快就会变得笨重。例如,如果有一个测试用例,其中数据从指定源文件读取,加载到目标,然后进行验证,可能还会移动到其他地方(例如 MSMQ 队列或数据库),您可能会发现文件名和文件夹名称在测试步骤中重复。此外,如果您已将此数据(文件夹名称、文件名、连接字符串等)存储在配置文件中,例如用于处理非 BizTalk 工件的 NUnit 测试,那么在测试 XML 文件中硬编码所有这些内容是不必要的,而且随着测试库的增长,维护起来会非常困难。
这就是“Context”对象大显身手的地方。
Context 对象
Context 对象代表一个在 BizUnit 测试步骤之间传递的状态对象。在 BizUnit 系统内部,Context 对象会被传递给每个单独的测试步骤。它不能在 TestFixture
类中初始化和使用,但在自定义步骤中可用。在与测试用例对应的 XML 文件中,可以使用指定的步骤(预先编写或自定义)读取和操作 Context。
在 BizUnit 系统内部,状态可以写入或从 Context 中读取。Context 还为测试步骤提供辅助方法,以一种一致的方式读取其配置,以及记录信息、警告、错误和数据。当测试执行时,输出窗口中提供的详细信息是由使用 Context
类生成的。因此,如果您正在编写自定义步骤,则应使用 Context
类中提供的方法来记录异常、错误和警告等。
您还可以使用它来存储诸如名称-值对之类的信息,这有助于自定义步骤的逻辑。(当然,这并非强制。由于自定义步骤仅仅是您自己的类,您可以根据需要定义和使用自己的私有成员变量。)
正如我在上面的“基础知识”部分提到的,大型脚本很快就会变得难以维护,因此 Context 的另一个重要用途是存储跨测试步骤使用的配置数据。对于仅使用少量步骤的非常简单的测试用例,这可能看起来不是很有用,但随着测试用例越来越大,并且数据出现重复时,将参数存储在 Context 中就变得至关重要。
使用 Context:基础知识
思考 Context 的一个简单方法是,它可以像 NAnt 属性一样使用。在 NAnt 中,我们会声明一个属性变量,赋予它一个特定名称并存储一个值。然后在所有任务中,您只需使用 ${propertyname} 引用该属性,系统就会获取相应的值。
在 BizUnit 中,开箱即用地,我们首先创建一个 FileValidate
步骤来指向我们的配置文件。此步骤将文件加载到内存中(为避免丢失文件,请将 <DeleteFile>
设置为 false)。之后,我们可以使用 XmlContextLoader
、TextContextLoader
或 RegExContextLoader
从源文件中提取数据。
上下文加载器的选择取决于数据的格式。XmlContextLoader
要求您提供 XPath 表达式,允许它导航源数据并提取值。RegExContextLoader
要求提供一个正则表达式应用于源数据(严格来说,只要正则表达式正确,文件是 XML 还是文本都无关紧要)。TextContextLoader
略有不同。它的工作方式类似于 Substring()
方法和旧的 VB6 Mid$()
函数,查找指定的模式、索引和字符串长度来从文件中检索数据。(有关详细示例,请参阅下文。)
哪些步骤支持上下文配置数据?
大多数步骤(一个显著的例外是 DotNetObjectInvoker
)都支持配置数据。这在文档中并不十分清楚(尽管有时会有些提示)。在 XML 节点中(对应于步骤中的任何元素),我们需要使用属性“takeFromCtx = <contextkeyname>
”,系统将在执行步骤时查找上下文。
例如,考虑以下 FileCreateStep
。在此,已使用适当的上下文加载器加载了上下文。
<TestStep assemblyPath=""
typeName="Microsoft.Services.BizTalkApplicationFramework.BizUnit.FileCreateStep">
<SourcePath takeFromCtx="sourcefilename"></SourcePath>
<CreationPath takeFromCtx="targetfilename"></CreationPath>
</TestStep>
在 SourcePath
和 CreationPath
元素中,我们使用 takeFromCtx 属性并为其提供系统应查找的上下文键名。系统会在运行时进行替换。
与 NAnt 的不同之处
上下文变量的一个非常重要的限制是,除了简单的上下文查找之外,没有运行时“宏扩展”行为。
例如,在 NAnt 任务中,我们可以将各种属性(以及系统属性)连接在一起,所以如果我们想要一个完整的文件名,我们可以使用类似以下的内容:
${source.path}\${file.name}.${file.extension}
系统随后会将属性名称替换为相应的值,并使用提供的符号(如 \ 和文件扩展名之前的点)来获得完整的文件名。目前,这在 BizUnit 中无法实现,尽管可以通过重载 Context
类的方法来应用宏扩展,当检测到 $ 和 {} 等适当符号时。
XmlContextLoader
概述
XmlContextLoader
会评估源数据的 XPath 表达式,并将值添加到上下文中。
示例
以下是此测试步骤的 XML 表示的示例。在此示例中,已使用文件验证步骤(未显示)验证了文件的存在,并且该文件包含所有上下文变量。
本示例中使用的实际文件包含在源代码中,位于 TestData 文件夹内。文件名是 XmlContextData.xml。内容如下所示:
<Variables>
<sourcefilename>c:\temp\test.xml</sourcefilename>
<targetfilename>c:\temp\testtarget.xml</targetfilename>
<connectionstring>Persist Security Info=False;Integrated Security=SSPI;
database=Northwind;server=(local);Connect Timeout=30</connectionstring>
<sourcefolder>c:\temp\</sourcefolder>
<targetfolder>c:\temp\</targetfolder>
</Variables>
现在,当我们使用 ContextLoader
时,我们可以使用正确类型的上下文加载器(如 XmlContextLoader
或 RegExContextLoader
)将文件的所有内容加载到上下文中。
这里,我们使用的是 XmlContextLoader
,XPath 表达式代表源文件中数据的导航路径(即,'Variables
' 是顶级元素,'sourcefilename
' 和 'targetfilename
' 是包含我们要加载到上下文中的值的元素)。每个 XPath 上下文键元素都会导致执行等同于 Context.Add(key,object)
的操作。
<ContextLoaderStep assemblyPath=""
typeName="Microsoft.Services.BizTalkApplicationFramework.BizUnit.XmlContextLoader">
<XPath contextKey="sourcefilename">
/*[local-name()='Variables']/*[local-name()='sourcefilename']
</XPath>
<XPath contextKey="targetfilename">
/*[local-name()='Variables']/*[local-name()='targetfilename']
</XPath>
</ContextLoaderStep>
RegExContextLoader
概述
RegExContextLoader
会将正则表达式应用于源数据,并将值添加到上下文中。
示例
以下是此测试步骤的 XML 表示的示例。在此示例中,已使用文件验证步骤验证了文件的存在,并且该文件包含所有上下文变量。
本示例中使用的实际文件包含在源代码中,位于 TestData 文件夹内。文件名是 RegExTestData.txt。文件内容如下所示:
The BizTalk web site is here: http://www.microsoft.com/biztalk,
you can find out more about the product there.
现在,当我们使用 ContextLoader
时,我们可以使用正确类型的上下文加载器(如 XmlContextLoader
或 RegExContextLoader
)将文件的所有内容加载到上下文中。
这里我们使用 RegExContextLoader
并将各种项添加到上下文中。每个 RegEx
上下文键元素都将导致执行等同于 Context.Add(key,object)
的操作。(例如,名为 HTTP_Url
的上下文键现在将包含 http://www.microsoft.com/biztalk。)
<ContextLoaderStep assemblyPath=""
typeName="Microsoft.Services.BizTalkApplicationFramework.BizUnit.RegExContextLoader">
<RegEx contextKey="HTTP_Url">/def:html/def:body/def:p[2]/def:form</RegEx>
<RegEx contextKey="ActionID">/def:html/def:body/def:p[2]/def:form/def:input[3]</RegEx>
<RegEx contextKey="ActionType">/def:html/def:body/def:p[2]/def:form/def:input[4]</RegEx>
<RegEx contextKey="HoldEvent">/def:html/def:body/def:p[2]/def:form/def:input[2]</RegEx>
</ContextLoaderStep>
TextContextLoader
概述
TextContextLoader
的工作方式类似于 Substring
和旧的 VB6 Mid$
函数。它会在源数据中搜索指定的字符串,然后从指定位置和指定长度返回子字符串。
示例
以下是此测试步骤的 XML 表示的示例。在此示例中,已使用文件验证步骤(未显示)验证了文件的存在,并且该文件包含所有上下文变量。本示例中使用的实际文件包含在源代码中,位于 TestData 文件夹内。文件名是 TextContextLoaderDemo.txt。该文件包含以下两行:
source=c:\temp\test.xml"
target=c:\temp\testtargetnew.xml"
在以下代码片段中,我们使用 TextContextLoader
并将各种项添加到上下文中。
<ContextLoaderStep assemblyPath=""
typeName="Microsoft.Services.BizTalkApplicationFramework.BizUnit.TextContextLoader">
<Item contextKey="sourcefilename" searchString="source" skipNumber="1" stringLength="16" />
<Item contextKey="targetfilename" searchString="target" skipNumber="1" stringLength="25" />
</ContextLoaderStep>
现在发生的情况是,在设置名为“sourcefilename
”的上下文键时,系统会查找模式/搜索字符串“source”,并找到其 IndexOf
值。然后将其加到 skipNumber
(即 1)上,从而忽略数据行中的“=”符号。然后它查找接下来的 16 个字符(恰好是完整的路径名),并返回该字符。对于下一个上下文键“targetfilename
”,会发生相同的处理,在这种情况下,它会获取 25 个字符。
这一概念可以应用于源文件中的一个大型参数字符串(如果您不想费心创建 XML 文件并找出必要的 XPath 表达式)。
局限性
此步骤确实有一个限制,即返回值的字符串长度需要预先知道,这限制了开发者轻松更改测试数据的能力。可以创建此步骤的未来版本(或另一个自定义步骤),它允许灵活地加载上下文值数组。
ContextManipulatorStep
概述
ContextManipulator
用于操作 BizUnit Context 字段。根据文档,它可用于从一个或多个现有字段创建一个新字段。
示例
在以下示例中,创建了一个名为 newsource
的新上下文键,其值来自现有的上下文键。
<TestStep assemblyPath=""
typeName="Microsoft.Services.BizTalkApplicationFramework.BizUnit.ContextManipulatorStep">
<ContextItem contextKey="newsource">
<ItemTest takeFromCtx="sourcefilename"></ItemTest>
</ContextItem>
</TestStep>
Issue
开箱即用地,此步骤似乎不起作用,无论如何尝试。代码似乎并未正确编写。(我们最好编写自己的步骤来处理此问题。)我将很快为此编写一个自定义步骤,并发布在此网站上。
摘要
本文仅对 Context 的工作方式进行了简要概述,希望这能为您提供所需的理解来利用它。在我当前的一个集成项目中,我们编写了一些自定义步骤,并广泛使用 Context 来读取我们的 app.config 和 web.config 文件,事实证明它在消除冗余声明和硬编码方面非常有价值。如果您有任何问题,请发送电子邮件给我,我会尽快答复。祝您好运(如果您编写了任何自定义步骤或找到了使用 Context 的新技巧,请告知社区的其他成员)。
此步骤提供的代码存在几个 bug
修复 1:删除创建流读取器并读取到数据流末尾的代码,因为这样 XmlDoc
将无法加载数据(因为指针已移到末尾),并且 StreamReader
根本没有在任何地方使用。
修复 2:将 SelectSingleNode
代码更改为以下内容:
contextValue = doc.SelectSingleNode( xpathExp ).InnerText;