Moles 框架





5.00/5 (2投票s)
Microsoft® Moles 2010 教程。
引言
Microsoft® Moles 2010 是 Microsoft Visual Studio® 2010 的一个插件,它帮助您将 .NET Framework 代码与外部依赖项隔离开来,以便可靠地测试代码是否按预期执行。借助 Moles 框架,您可以将被测试代码调用的任何方法替换为使用委托的测试实现。
背景
Visual Studio Test Explorer 已经支持那些能够将单元测试集成到其软件开发实践中的开发人员。单元测试通过验证应用程序代码是否按照您的预期执行来帮助您确保程序的正确性。Test Explorer 在 Visual Studio 中提供了一种灵活而有效的方式来运行您的单元测试并查看其结果。
那么,当单元测试如此出色时,为什么还需要 Moles 框架呢?
我相信所有开发人员都一定为他们的代码编写过单元测试用例。接受像 Moles 框架这样新的东西对他们来说并不容易。所以,让我们分析一下当前单元测试概念存在的问题。
当我们为某个类编写单元测试用例时,我们实际上是想测试该特定类的功能,而不是它所依赖的所有代码。例如,假设您编写了一个类 X,它依赖于类 Y,即类 X 调用类 Y。因此,当您为类 X 编写测试用例时,它将内部调用类 Y。
我们可以将单元测试的局限性总结为:
- 使测试成为集成测试。
- 要运行测试用例,您现在不仅需要理解类 X 的复杂性,还需要理解类 Y 的复杂性。
- 如果类 Y 发生任何更改,类 X 的单元测试用例也需要更改。
- 如果我们的类 Y 调用了一个数据库或文件,而我们没有权限访问它,那么即使测试类 X 的目的已经达到,我们的测试用例也可能会失败。
因此,为了克服这些问题,Microsoft 推出了 Moles 框架。
Moles 框架先决条件
在我们开始学习之前,请确保您拥有创建第一个 mole 测试应用程序所需的所有要素。
- Windows® 7、Windows Vista® 或 Windows Server® 2008 R2 或更高版本的操作系统
- Visual Studio 2010 Professional
Microsoft Moles 也适用于 Visual Studio 2008 Professional;本教程假设您的版本支持 Visual Studio Unit Testing framework。
- Microsoft Moles 2010
所以,当您具备所有先决条件后,就可以开始动手了。
Moles 框架
Moles 框架允许我们在不同情况下创建代码依赖项的替代类,以便孤立地测试单个组件,并使测试更健壮和可扩展。
它允许将任何 .NET 方法替换为委托。在单元测试的上下文中,您可以使用 Moles 隔离环境依赖项,如时间、文件系统、数据库等,即使这些依赖项是通过静态方法或密封类型硬编码的。
- 一种常见的方法是使用名为“测试存根 (test stubs)”的虚拟实现——用于当前未被测试的组件。例如:接口
- 对于不允许使用测试存根的代码,Moles 框架允许“重定向 (detouring)”硬编码的依赖项。例如:静态方法
存根类型 (Stub type)
在 Moles 框架中,它特指为接口和非密封类生成的类型,允许您通过附加委托来重新定义行为。
Moles 类型 (Mole type)
当重构不可行时,我们可以使用运行时插桩 (runtime instrumentation) 来重定向方法。为此,我们使用 Moles 框架创建 Moles 类型,这些类型利用了运行时插桩。它们在处理具有静态方法、密封类型或非虚方法的 API 时很有用。
安装 Moles
- 下载 Moles 开始安装
- 与所有 Windows 软件一样,接受运行软件的提示。
- 在“设置向导欢迎”页面上单击下一步,并接受许可协议以继续。
- 选择设置类型——典型 (Typical)、自定义 (Custom) 或完整 (Complete)——然后单击安装 (Install)。
使用代码
为了使用 Moles 框架,我们将首先创建示例应用程序和测试项目。
任务 1:为存根类型创建应用程序项目
创建示例应用程序项目
创建类库项目
- 在 Visual Studio 中,单击文件 (File) >新建 (New) >项目 (Project)。
- 在新建项目 (Add New Project) 对话框的左侧窗格中,单击Visual C#。
- 在新建项目 (Add New Project) 对话框的右侧窗格中,单击类库 (Class Library)。
- 将项目重命名为StubsTutorial,然后单击确定 (OK)。
为类库创建测试
- 打开新创建的 StubsTutorial 解决方案(如果尚未打开)。
- 在 Visual Studio 中,单击文件 (File) >新建 (New) >项目 (Project)。
- 在新建项目 (New Project) 对话框的左侧窗格中,展开Visual C#,然后单击测试 (Test)。在中间窗格中,单击测试项目 (Test Project)。在解决方案 (Solution) 下拉列表中,单击添加到解决方案 (Add to solution)。
- 将项目重命名为StubsTutorialTest,然后单击确定 (OK)。
- 在“解决方案资源管理器”中,右键单击StubsTutorialTest 下的引用 (References) 节点,然后从上下文菜单中选择添加引用 (Add Reference)。单击项目 (Projects) 选项卡,单击StubsTutorial,然后单击确定 (OK)。
- 在 Visual Studio 中,打开测试项目 StubsTutorialTest,打开“引用”节点,右键单击 StubsTutorial 引用节点,然后选择添加 Moles 程序集 (Add Moles Assembly)。
- 现在向项目中添加了一个 .moles 文件,它是一个简单的 XML 文件,指定了应该被 Moles 和 Stub 的程序集。打开文件查看。
现在,您已经完成了使用 Moles 框架的基本架构。
为了了解框架的实际工作原理,让我们向上面创建的类库添加一个类。
- 在 Visual Studio 中,右键单击StubsTutorial 节点,单击添加 (Add),然后单击类 (Class)。
- 在添加新项 (Add New Item) 对话框的左侧窗格中,单击Visual C# 项目 (Visual C# Items)。在中间窗格中,单击类 (Class)。然后将类重命名为Virtual.cs,并单击添加 (Add)。
- 打开Virtual.cs 类,并将类的内容替换为以下代码片段。
public class Virtual
{
public virtual bool IsEvenNumber(int numberToCheck)
{
returnnumberToCheck % 2 == 0;
}
public string SomeProcessVitual(int someNumber)
{
// This method does a lot of things.
// one of those is to check if the parameter is even
var isEven = this.IsEvenNumber(someNumber);
if (isEven)
{
return "Even";
}
return "Odd";
}
}
在上面的类中,您有两个方法。SomeProcessVitual
调用 IsEvenNumber
方法。因此,如果您为 SomeProcessVitual
方法编写传统的单元测试用例,测试用例将内部调用 IsEvenNumber
方法,从而将其变成集成测试。为了解决这个问题,我们将在这里为 IsEvenNumber
方法创建一个虚拟实现,从而可以消除对该方法的依赖。
因此,当您的 SomeProcessVitual
方法内部调用 IsEvenNumber
方法时,它将不会执行实际的实现,而是执行我们创建的虚拟实现。
要实现上述目标,请向上面创建的测试项目添加一个单元测试用例。
- 在 StubsTutorialTest 项目中,添加一个测试类,将其重命名为TestReaderTest.cs,然后打开它进行编辑。
- 用以下代码片段替换文件内容,该代码片段添加了带有 Arrange、Act 和 Assert 步骤的单元测试。
[TestClass]
public classVirtualTest
{
[TestMethod]
public voidVirtual_SomeProcess_StubTest()
{
// Arrange
varvirtualM = new SVirtual();
virtualM.IsEvenNumberInt32 = delegate(int n)
{
returntrue;
};
// Act
varresponse = virtualM.SomeProcessVitual(11);
// Assert
Assert.AreEqual("Even", response,
"Should return Even, even though we passed in an odd number.");
}
}
现在,尝试调试您的测试类。您会注意到,当您调用 SomeProcessVitual
时,它不会调用 IsEvenNumberInt32
方法的实际实现,而是调用您在上面的测试类中创建的虚拟实现。
那么,如果无法重新定义您的方法定义,例如,您将如何为静态方法创建虚拟实现?为此,Microsoft 为我们提供了 Moles 类型,它允许我们使用运行时插桩来重定向方法。
为了使用 Mole 类型,我们将使用之前创建的项目。
- 在 Visual Studio 中,从前一项任务的项目开始,向项目中添加一个类:StaticMethod。
- 在此处添加以下代码
publicclass StaticMethod
{
public staticint MyMethod(intn)
{
returnn;
}
}
- 在测试项目中添加一个类:StaticMethodTest。
- 将以下代码添加到类中。
[TestClass()]
public class StaticMethodTest
{
[TestMethod()]
[HostType("Moles")]
public voidStatic_MyStaticMethodTest()
{
// arrange
var content1 = 21;
MStaticMethod.MyMethodInt32 = delegate(int n1)
{
return content1;
};
// act
var test1 = StaticMethod.MyMethod(11);
// assert
Assert.AreEqual(content1, test1);
}
}
由于 Moles 类型需要运行时插桩,Moles 运行时在 [HostType("Moles")]
下运行。如果您从测试中删除了 [HostType("Moles")]
,代码将不会被插桩,Moles 将抛出异常:“Moles 要求测试在一个插桩过的进程中运行。”
Moles 框架提供了强类型包装器,允许我们将任何 .NET 方法重定向到用户定义的委托。这些包装器称为 Moles 类型,以生成它们的框架命名。已被这样包装的方法称为已被 Moles 处理过的方法。
通常,重构是隔离的首选方法,因为它不需要运行时插桩。然而,重构通常不是一个选择——在这种情况下,运行时插桩可能是唯一的解决方案。
关注点
Mole 类型和 Stub 类型命名约定
命名空间
- Moles 后缀添加到命名空间。例如:
System.Moles
命名空间包含System
命名空间的 Mole 类型。 Global.Moles
包含空命名空间的 mole 类型。
类型名称
- M 前缀添加到类型名称以构建 mole 类型名称。例如:MExample 是
Example
类型的 mole 类型。 - S 前缀添加到类型名称以构建 stub 类型名称。例如:SIExample 是
IExample
类型的 stub 类型。
类型参数和嵌套类型结构
- 泛型类型参数被复制。
- 嵌套类型结构为 mole 类型复制。
功能 |
Stub 类型 |
Mole 类型 |
重定向机制 |
虚方法重写 |
运行时插桩 |
静态方法、密封类型 |
否 |
是 |
内部类型 |
是
|
是 |
私有方法 |
否 |
是 |
静态构造函数和析构函数 |
否 |
是 |
性能 |
快速 |
较慢 |
抽象方法 |
是 |
否 |