如果你想测试一个装饰器,你不必模拟被装饰的方法DoSomething,而是整个被装饰的类。
首先你必须修复你的类设计和装饰器实现:
现在装饰器在构造函数中接受他自己的装饰器类型的实例,这没有意义。如果它是一个装饰器,它必须接受 decorated 类型的实例。
这意味着您必须为要实现的所有装饰类型引入一个接口。在这个例子中,它被命名为IDecorated。
目标是进行如下所示的单元测试:
public class UnitTest
{
public void TestDecorator()
{
IDecorated mockOfIDecorated = new DecoratedMock();
DecoratorBase decoratorBaseTest = new DecoratorBaseTest(mockOfIDecorated);
// Use decoratorBaseTest instance to test its public members
}
}
为了使模拟成为可能,引入一个装饰类型的接口:
public interface IDecorated
{
void DoSomething();
}
这将更改并修复装饰器类型的构造函数的签名。也不要实现装饰器的 DoSomething 虚拟,因为这会导致不良行为(例如,扩展类忘记将调用委托给装饰类型实例)。因此virtual 已被删除。要使子类型的扩展行为可覆盖,只需添加一个抽象方法:
public abstract class DecoratorBase : IDecorated
{
private IDecorated decorated;
// The decorator should always accept an abstract type
// or interface of the type to be decorated
public DecoratorBase(IDecorated decorated)
{
this.decorated = decorated;
}
public void DoSomething()
{
// Delegate calls to the decorated class instance and ...
this.decorated.DoSomething();
// ... add functionality by invoking additional members.
// Making this member abstract adds customization for subtypes
DoSomethingToExtendTheDecoratedBehavior();
}
public object DoSomethingDecoratorSpecific()
{
}
protected abstract void DoSomethingToExtendTheDecoratedBehavior();
}
由于你不能直接测试一个抽象类,你需要提供一个可以被测试类实例化的测试实现:
public class DecoratorBaseTest : DecoratorBase
{
public DecoratorBaseTest(IDecorated mockOfIDecorated) : base(mockOfIDecorated) { /* ... */ }
#region Overrides of DecoratorBase
protected override void DoSomethingToExtendTheDecoratedBehavior()
{
// Do nothing here because the unit test
// tests only public members of DecoratorBase
}
#endregion
}
IDecorated 的模拟类型。对此类型调用 DoSomething() 没有任何作用:
public class DecoratedMock : IDecorated
{
#region Implementation of IDecorated
public void DoSomething()
{
// Do nothing since this is a mock
}
#endregion
}
现在装饰器实现已经固定,您可以轻松地测试装饰器类。