使用 Unity 应用程序块进行单元测试






3.86/5 (5投票s)
使用 Unity 应用程序块执行单元测试
引言
本文将讨论如何使用 Unity Application Block 进行单元测试。Unity Application Block 提供了一个依赖注入容器。它作为 Microsoft Enterprise Library 的一部分提供,也可以单独下载。您可以从 这里 下载。
背景
单元测试最常需要一个测试程序和模拟对象/存根。测试程序将是 Visual Studio 提供的测试项目。现在,无需编写代码来创建待测单元并用相应的模拟对象或存根进行初始化,以便能够独立进行测试。这可以通过 Unity 实现,您可以专注于为待测单元编写更好的测试。
单元测试
自动化单元测试使开发人员和测试人员能够更快、更好地测试其代码中的逻辑错误。创建后,在代码更改时运行这些测试并添加更多测试,可以确保负责编写代码的开发人员没有引入任何缺陷。
在编写单元测试时,请牢记以下一些指南:
- 单一条件测试。
- 分别测试关注点,例如,业务层和数据访问层应有单独的测试。
- 最小化测试重叠。
- 缺陷定位,保持单元测试小巧,一旦出现缺陷,可以根据失败的测试轻松定位。
- 保持测试独立,Fresh Fixture 策略会有帮助。
- 自检测试,使用内置/自定义断言。
- 测试的目的应非常明确。
- 最小化不可测试的代码。
- 将测试逻辑排除在生产环境之外。
- 测试应该是可重复的。
- 先编写单元测试。这取决于是否使用测试驱动开发或先测试开发。
此外,还可以考虑一些组织单元测试的指南和模式,例如每个功能一个 `Testcase` 类,每个夹具一个 `Testcase` 类。
Unity Application Block
Unity Application Block 提供了一个依赖注入容器。依赖注入是一种基于分离行为与依赖解析的原则,实现松耦合的模式。Unity 是一个轻量级且可扩展的依赖注入容器,支持通过构造函数、属性和方法调用进行注入。它可以帮助解耦、简化应用程序的设计和测试。
设计

分层架构简化了自动化测试,简单的三层架构即可。在上图中,我展示了由 `CustomerFacade`、`CustomerManager` 组件分别实现的 `ICustomerFacade`、`ICustomerManager`。这是**接口编程**;接口提供了实现的契约或规范,所有类的实现都应符合该契约。我们需要在每个组件都有接口。在依赖注入方面,一个**依赖**的消费者。
Using the Code
在开始创建和配置 Unity 的步骤之前,需要安装 Enterprise Library 5.0 或 Microsoft Unity 2.0。
首先,我们以业务外观类 `CustomerFacade` 为例,它看起来是这样的:
public class CustomerFacade : ICustomerFacade
{
[Dependency]
public ICustomerManager CustomerManager { get; set; }
#region ICustomerFacade Members
public long CreateCustomer(Customer customer)
{
try
{
return CustomerManager.CreateCustomer(customer);
}
catch (Exception e)
{
throw new BusinessException("BusinessException Occurred", e);
}
}
#endregion
}
为简单起见,我采用了一个通用的示例。
需要通过 Unity 解析的属性应标记为 `DependencyAttribute`。在上面的代码中,`CustomerManager` 属性具有此属性。
`CustomerFacade` 实现 `ICustomerFacade` 接口,从而将行为与依赖分离。
public interface ICustomerFacade
{
long CreateCustomer(Customer customer);
}
现在,我们将看到如何借助 Unity Application Block 测试 `CustomerFacade`。
步骤 1:创建测试项目并添加引用
- 在 Visual Studio 中向解决方案添加一个测试项目。
- 添加对 `Microsoft.Practices.Unity`、`Microsoft.Practices.Unity.Configuration`、`System.Configuration` 的引用。
步骤 2:添加测试类
- 在步骤 1 中创建的测试项目中添加一个测试类。
- 导入命名空间 `.Practices.Unity`、`Microsoft.Practices.Unity.Configuration`、`System.Configuration`。
可以使用 Visual Studio 2008 Professional 自动化步骤 1 和步骤 2。
- 右键单击方法,选择“创建单元测试…”
- 在对话框中,选择要包含在自动 `Test` 类和方法生成中的方法。
- 选择现有或新建测试项目。
- 点击“确定”
步骤 3:向步骤 1 中创建的测试项目添加 App.config 文件。
步骤 4:设置 App.config 文件中的 Unity 配置部分。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="unity"
type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
Microsoft.Practices.Unity.Configuration"/>
</configSections>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity"
<assembly name="UnitTestingWithUnity" />
<assembly name="UnitTestingWithUnity.Test" />
<namespace name="UnitTestingWithUnity.BusinessFacade" />
<namespace name="UnitTestingWithUnity.Business" />
<namespace name="UnitTestingWithUnity.Data" />
<namespace name="UnitTestingWithUnity.Test.Mock" />
<container>
<register type="ICustomerManager" mapTo="MockCustomerManager" />
<register type="ICustomerDataAccess" mapTo="MockCustomerDataAccess" />
</container>
</unity>
</configuration>
理解配置文件
- 为配置文件添加 `UnityConfigurationSection` 引用。
- 添加解析接口和实现类所需的所有程序集和命名空间到配置中。
- `MockCustomerManager` 类实现 `ICustomerManager` 接口。
- 使用容器注册要解析的接口及其对应的类映射。
步骤 5:编写测试
`TestInitilize` 方法应初始化 unity 容器以加载上一步中创建的 unity 配置。
private IunityContainer unityContainer;
[TestInitialize()]
public void MyTestInitialize()
{
unityContainer = new UnityContainer();
unityContainer.LoadConfiguration();
}
Unity 容器也可以在不使用配置文件的情况下进行初始化。代替 `LoadConfiguration`,应在 unity 容器上使用 `RegisterType` 方法。
unityContainer.RegisterType<ICustomerManager, CustomerManager >();
unityContainer.RegisterType<ICustomerDataAccess, CustomerDataAccess>();
在测试方法中,我们将使用刚刚创建的 unity 容器来解析 `CustomerFacade` 类。
/// <summary>
///A test for CreateCustomer
///</summary>
[TestMethod()]
public void CreateCustomerTest2()
{
ICustomerFacade target = new CustomerFacade();
target = unityContainer.BuildUp<ICustomerFacade>(target);
Customer customer = new Customer{Name="Test 1", Address = "Test Address 1" };
long custId = target.CreateCustomer(customer);Assert.AreEqual(custId, 1);
}
为了让 Unity 解析所有依赖项,我们应该使用 Unity 提供的依赖注入容器来构造和初始化对象。这可以通过两种方式完成:
使用 new 操作符创建对象,然后使用 unity 容器上的 `BuildUp` 方法解析所有依赖项。
ICustomerFacade target = new CustomerFacade();
target = unityContainer.BuildUp<ICustomerFacade>(target);
或者,使用 `Resolve` 泛型方法一次性创建和解析依赖项。
ICustomerFacade target = unityContainer.Resolve<CustomerFacade>();
步骤 6:执行刚刚创建的测试。
就是这样。
参考文献
- MSDN – http://msdn.microsoft.com
- Unity on MSDN - http://msdn.microsoft.com/en-us/library/ff663144.aspx
- Enterprise Library on MSDN - http://msdn.microsoft.com/en-us/library/ff632023.aspx
- XUnit Patterns - http://xunitpatterns.com/
- Wikipedia Dependency Injection - http://en.wikipedia.org/wiki/Dependency_injection
历史
- 2011年2月12日:初稿