Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 重构使用新运算符的代码,使其更易于测试_C#_.net_Unit Testing_Tdd_Factory Pattern - Fatal编程技术网

C# 重构使用新运算符的代码,使其更易于测试

C# 重构使用新运算符的代码,使其更易于测试,c#,.net,unit-testing,tdd,factory-pattern,C#,.net,Unit Testing,Tdd,Factory Pattern,我有以下代码,正在尝试对其进行单元测试: 公共重写IRenderable GetRenderable() { var val=SomeCalculationUsingClassMemberVariables(); 返回新的equalationRenderable(val); } 看起来我想在这里使用一个工厂,这样我就可以将IRenderable的创建从这个类中分离出来。问题是,我有许多这样的类,它们创建不同的iUnderable,这些iUnderable以不同的方式构造,因此我需要为每个类实

我有以下代码,正在尝试对其进行单元测试:

公共重写IRenderable GetRenderable()
{
var val=SomeCalculationUsingClassMemberVariables();
返回新的equalationRenderable(val);
}


看起来我想在这里使用一个工厂,这样我就可以将IRenderable的创建从这个类中分离出来。问题是,我有许多这样的类,它们创建不同的iUnderable,这些iUnderable以不同的方式构造,因此我需要为每个类实现一个新的工厂方法。解决此问题的最佳方法是什么?

根据混凝土IRenderable构造函数的均匀性,您可以使用以下模式进行工厂创建

public IRenderable CreateInstance<T>(object calculation) where T : IRenderable 
{
   Activator.CreateInstance<T>(new[] { calculation });
}

更好的方法可能是简单地按原样对代码进行单元测试,而不是重构代码。从技术上讲,这可以通过使用合适的模拟工具来实现,如TypeMock隔离器或Microsoft Moles(还有第三个工具,我现在不记得了)。

问得好

首先,到处使用AbstractFactorys的感觉有点异味,因为DI容器的使用方式不“正确”,或者设计可能会有所改进

但有时我也会遇到这个问题。我看到以下情况:

  • 使用AbstractFactory/Factory并注入它。对于C#,您必须有一个优势,即可以传递委托,充当创建实例的接口
  • “new”可以,只需测试“new”的输出即可
  • 在提取的方法中存根“new”的调用(hacky!!)
  • 注射已经提到了,所以我不再重复了。我更喜欢Java,所以请原谅一些语法错误

    测试“new”的输出 如果“新”创建的实例是域对象而不是服务,我通常会使用它。因为它是在方法中直接返回的,所以我可以使用测试测试直接输出

    Prod-Code:
    ...
    public override IRenderable GetRenderable()
    {
      var val = SomeCalculationUsingClassMemberVariables();
      return new EquationRenderable(val);
    } 
    
    Test Case:
    ...
    [Test]
    public void test_new()
    {
      SUT sut = ...;
      IRenderable r = sut.GetRenderable();
      assertTrue(r instanceof EquationRenderable);
    }
    
    
    Stub调用“new”本身 只有当您以某种方式将其作为返回值时,才能测试上面的直接输出。如果代码的“副作用”是间接输出,而您无法通过返回值直接感知,那么事情就会变得更加复杂。若是这样的话,我经常提取新创建的方法,然后在测试中控制它。这很恶心,我更多地使用它来保证测试的安全,并在以后开始更多的重构(DI和工厂)。我有时会在遗留代码中这样做,在遗留代码中,直接使用“new”创建服务,而在没有测试的情况下重构到DI风险太大

    Prod-Code:
    ...
    public override IRenderable GetRenderable()
    {
      var val = SomeCalculationUsingClassMemberVariables();
      return createEquationRenderable();
    } 
    
    public IRenderable createEquationRenderable() 
    {
       return new EquationRenderable(val);
    }
    
    
    Test Case:
    ...
    
    class Stubbed : SUT 
    {
       boolean called = false;
    
       public override EquationRenderable createEquationRenderable() 
       {
          called=true;
          return MyMock();
       }
    }
    
    [Test]
    public void test_new()
    {
      Stubbed sut = new Stubbed();
      sut.GetRenderable();
      assertTrue(sut.called);
      // do further stuff on MyMock
    }
    
    我知道,这个例子有些过分,有点毫无意义,只是为了描述这个想法。我确信上面提到的C#模拟框架可以简化。无论如何,在这里测试直接输出的返回值是更简单和更好的方法


    也许您有一个更详细的示例?

    我也考虑过这种方法,但如果传入错误的参数,似乎会导致查找运行时错误变得更困难。此外,客户无法知道创建特定实例需要哪些参数,因为具体的构造函数根本不是很统一。也许我在这里要求的太多了。当客户端调用CreateInstance方法时,他们必须传递一个类型,并且在这一点上了解实现(rtfm:)。确实,您不能对参数执行编译器检查,但这与客户端无法在文档中读取要传递的参数不同。我想说的是,除非你限制自己在IRenderable类中允许使用什么构造函数,否则没有办法在工厂模式中对参数进行编译器检查。我添加了一个小片段,所以运行时错误可能会使无效参数更有意义。好的,我也差不多就是在那里结束的。工厂模式是否通常用于这种场景,其唯一目的是增加可测试性?是的,工厂模式或依赖项注入是使事情更易于插入和测试的目的。我可以向您保证,DI将采用相同的方法,在运行时将参数匹配到正确的构造函数,因此切换到DI没有任何帮助。。。这有点棘手,因为您没有提供太多的代码,但是您没有让需要IRenderable对象的类自己创建它,而是将这样的对象传递给它。通过这种方式,您可以实例化有问题的类,将一些伪IRenderable对象传递给它,并测试您需要测试的任何方法。GetRenderable函数基本上返回调用它的类的字符串版本,因此它几乎就像序列化对象一样。我不确定如何从它所在的对象中提取,因为Renderable是基于成员变量创建的。顺便说一句,谢谢你的意见!现在我要测试的是返回类型是否正确,如第一个示例中所示。问题是,大多数逻辑都是在计算传递到所创建对象中的参数时进行的,因此不会捕获其中的错误。我并不是完全反对你的第二个想法,但嘲笑我正在测试的东西似乎有点不礼貌。正如你提到的,我也感觉到设计可以改进,但我很难看到如何修复它。我也可以测试返回类型上的值是否正确,但我也在测试该类型的实现,如果这种情况发生变化,它将破坏不同领域的测试。现在,我想我只是要将传入参数的属性设置为公共的(我想我的API将来无论如何都需要),这样我就可以在返回的对象上针对该公共属性进行测试。不过,我会继续看看我能做些什么来改进设计。我真的很感谢你的详细回复!没问题:)对于单元测试和处理代码库,我真的可以推荐一些书(xUnit测试模式)和(有效地处理遗留代码)
    Prod-Code:
    ...
    public override IRenderable GetRenderable()
    {
      var val = SomeCalculationUsingClassMemberVariables();
      return new EquationRenderable(val);
    } 
    
    Test Case:
    ...
    [Test]
    public void test_new()
    {
      SUT sut = ...;
      IRenderable r = sut.GetRenderable();
      assertTrue(r instanceof EquationRenderable);
    }
    
    
    Prod-Code:
    ...
    public override IRenderable GetRenderable()
    {
      var val = SomeCalculationUsingClassMemberVariables();
      return createEquationRenderable();
    } 
    
    public IRenderable createEquationRenderable() 
    {
       return new EquationRenderable(val);
    }
    
    
    Test Case:
    ...
    
    class Stubbed : SUT 
    {
       boolean called = false;
    
       public override EquationRenderable createEquationRenderable() 
       {
          called=true;
          return MyMock();
       }
    }
    
    [Test]
    public void test_new()
    {
      Stubbed sut = new Stubbed();
      sut.GetRenderable();
      assertTrue(sut.called);
      // do further stuff on MyMock
    }