PostSharp在单元测试中的应用

在面向对象编程中,模拟对象是一种重要的测试技术,它允许开发者在不依赖于复杂环境的情况下测试代码。模拟对象通常用于测试驱动开发(TDD)中,以便在开发过程中验证其他对象的行为。然而,为了使用这种技术,对象必须是“可模拟的”。在C#中,这通常意味着具有接口或抽象的实例对象,例如在NMock中使用的。静态对象和静态方法通常不可模拟。

PostSharp是一个强大的工具,它可以拦截任何方法或属性的使用。通过PostSharp,即使是静态方法和属性也可以被模拟,这在传统的模拟框架中是不可能的。PostSharp可以在不实际调用被拦截的方法或属性的情况下返回新的值。

PostSharp的工作原理是在编译时透明地修改代码,这意味着使用PostSharp编译的二进制文件与未使用PostSharp编译的二进制文件不同。这种差异可以通过工具如Reflector或ILDASM来揭示。幸运的是,PostSharp确保修改后的二进制文件与未修改的版本运行相同,并且更改的逻辑对调试器隐藏。

使用PostSharp进行单元测试的基本步骤如下:

  1. 为目标方法添加MockMethod属性,为目标属性添加MockProperty属性。
  2. 设置TestApparatus,添加拦截条件和返回值。
  3. 编写涉及模拟方法和属性的测试,并运行测试。

以下是一个简单的测试示例:

public class StaticProvider { [MockMethod] public static bool Provide(int input) { throw new NotImplementedException("Static Provider Not Implemented"); } } [TestMethod] [Description("With both methods mocked, the test should pass")] public void AllSuccess() { TestApparatus.MockStatic<bool, int>(StaticProvider.Provide).Any().Return(true); TestApparatus.MockInstance<InstancedProvider, bool, int>(i => i.Provide).Expecting(1).If(a1 => a1 >= 0).Return(true); SampleObject sample = new SampleObject(); sample.Methods(); }

在上述代码中,第5行告诉框架拦截对静态方法StaticProvider.Provide(int)的任何调用,并返回true。第6行让框架拦截实例方法InstancedProvider.Provide(int),当输入值大于0时返回true。它还告诉框架在测试期间只期望一次调用。

以下示例展示了如何模拟属性:

[TestMethod] [Description("Mocking a specific property")] public void MockingProperty() { const int mocked = 12034; TestApparatus.MockProperty(() => StaticProvider.Value).Any().Return(mocked); TestApparatus.MockProperty((InstancedProvider i) => i.Value).Expecting(1).Any().Return(mocked); SampleObject sample = new SampleObject(); Assert.AreEqual(mocked, sample.Values()); }

第6行模拟静态属性StaticProvider.Value,使其在任何时候都返回模拟值。第7行模拟实例属性InstancedProvider.Value,使其在任何时候都运行模拟值。

在框架内部,所有模拟的方法和属性都被内部拦截,但只有符合指定条件的那些被覆盖。框架所做的只是记录拦截的要求,并在运行时执行。

该项目使用Visual Studio 2008和.Net 3.5完成,已验证在Visual Studio 2012下无需任何更改即可工作。必须安装PostSharp,并且可能需要刷新对PostSharp库的引用。

使用PostSharp进行模拟的一个关键点是,它需要模拟的方法和属性的源代码。这意味着二进制文件在编译时被PostSharp修改。然而,PostSharp保证了修改后的二进制文件与未修改的版本运行相同,并且更改的逻辑对调试器隐藏。

另一个问题是,被测试的二进制文件与要部署的二进制文件不同。它们是等效的,这是PostSharp保证的。因此,主要的担忧是不要将测试版本部署到生产环境中。在提供的框架中,MockMethodAttribute和MockPropertyAttribute包含条件语句,以在非调试构建中将自己更改为简单的属性。PostSharp不会识别这些简单的属性,因此生产二进制文件保持不变。这有助于将测试二进制文件与生产二进制文件分开。

与基于接口的模拟测试框架(如NMock和Rhino Mock)相比,这种方法可以自由地模拟任何静态对象。测试目的不需要接口,这对于最初没有为模拟测试设计的代码来说是一个解脱。与基于分析器的模拟测试框架(如JustMock)相比,这种方法在性能上有优势,因为只有带有属性的方法和属性才会触发拦截。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485