【问题标题】:How to mock an abstract class using the decorator pattern and containing a constructor that takes an instance of itself如何使用装饰器模式模拟抽象类并包含一个构造函数,该构造函数采用自身的实例
【发布时间】:2019-08-10 19:26:16
【问题描述】:

首先,我想澄清一下,这个问题不是this 的重复,因为当你有一个抽象类时情况会有所不同。

我的情况是存在基类

public abstract class FooBase
{
     public FooBase(FooBase inner) { /* ... */ }

     public virtual void DoSomething() { /* ... */ }
}

它在不同的程序集中,我想模拟DoSomething() 的行为。问题是,如果我尝试创建一个模拟

public class MockFoo : FooBase
{
    public MockFoo(...) : base(/*I have to put a FooBase in here*/) { /* ... */ }
}

我不能将new MockFoo() 传递给base,因为它会导致无限递归。我的任何其他想法(例如创建另一个MockFooOuter)都无法解决无限递归问题。

【问题讨论】:

  • 内层可以为空吗?
  • 如果没有 minimal reproducible example 澄清您的具体问题或其他详细信息以准确突出所做的工作,就很难重现问题,从而更好地理解所询问的内容。
  • 你需要展示一个目标类的更好的例子。

标签: c# unit-testing oop design-patterns polymorphism


【解决方案1】:

我同意恩科西的观点。您需要将任何(如建议的 null)值传递给 base,这不会导致 FooBase 构造函数中的异常。

【讨论】:

    【解决方案2】:

    如果你想测试一个装饰器,你不必模拟被装饰的方法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
    }
    

    现在装饰器实现已经固定,您可以轻松地测试装饰器类。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-28
      • 2014-02-25
      • 2013-08-12
      • 2017-08-25
      • 2021-10-16
      • 1970-01-01
      • 2018-11-24
      • 1970-01-01
      相关资源
      最近更新 更多