使方法“内部”去除依赖性(用于单元测试) - 一个好的做法?有更好的方法吗?

时间:2011-02-02 10:11:55

标签: c# unit-testing

我有一个课程如下。

public class MyClass  
{  
    public MyMethod()  
    {  
      int x = CalculateSomething();  
    }

   private int CalculateSomething()  
   {  
      // Do something and return an int    
      return 100;  
   }  
}  

为了对此进行单元测试,我添加了[assembly: InternalsVisibleTo("MyTests")]并将私有方法更改为内部虚拟。 在单元测试项目中,我创建了一个类MockMyClass并创建了私有方法,如下所示。

public class MockMyClass : MyClass  
{  
   public bool MadeHappyNoise {get; set;}
   internal override int CalculateSomething()  
   {  
      MadeHappyNoise = true;  
      return base.CalculateSomething();  
   }  
}

单元测试现在如下

[TestMethod()]
public void WasCalculateSomethingCalledOK()  
{  
   MockMyClass mk = new MockMyClass();
   mk.MyMethod();  
   Assert.IsTrue(mk.MadeHappyNoise, "Oops...CalculateSomething not called...");  
}  

几个问题:这是删除依赖项的好方法吗?我个人不喜欢将方法从私有更改为内部但没有选项(除了使用Reflection之外)。此外,驻留在生产代码中的属性InternalsVisibleTo(“MyTests”)也不好。有人能指出我更好的解决方案吗?感谢。

5 个答案:

答案 0 :(得分:1)

嗯。我对该代码有一些问题,但我们会一次做一个。

  1. 为什么要测试MyMethod是否调用CalculateSomething?这是一个可能会改变的实现细节(如果它明天调用CalculateSomething2但除此之外还会做它应该做的事情?)。如果您想测试MyMethod的代码结构,请进行代码审查,而不是单元测试。

  2. 你说MyMethod很复杂,你想测试里面的代码流。如果里面有多个路径,你仍然需要为每个路径编写单元测试,那么为什么不能检查调用MyMethod的结果而不是检查它的内部?

    另一种想法是尝试将MyMethod重构为方法,使其更容易进行测试(如果你进行测试驱动开发,这几乎是自动的,如果你想进行严格的单元测试,我建议这样做。“后来测试“方法几乎总是导致代码很多更难以测试。”

  3. 如果你仍然想检查MyMethod的内部工作原理,也许你可以将你需要的私有方法重构为另一个类(在你的例子中说“计算”)。

    然后你可以使用模拟框架(比如RhinoMocks)来模拟那个类。该框架允许您定义您希望以什么顺序调用哪些函数以及它们应该返回什么函数。

    通常你使用模拟来减少单元测试的环境要求,但你也可以这样使用它们。

答案 1 :(得分:1)

你能否重构它是这样的:

public class MyClass  
{  
    private Calculator calculator;  

    public myMethod()  
    {  
      int x = calculateSomething();  
    }

    public void SetCalculator( Calculator c ){
        calculator = c;  
    }

   private int calculateSomething()  
   {  
        return calculator.CalculateSomething();
   }  
}  

然后将计算器作为单独的类并在MyClass上设置实例

public Class Calculator {

   public virtual int CalculateSomething()  
   {  
      // Do something and return an int    
      return 100;  
   }  
}

您可以让Calculator实现一个接口,然后使用不同的Calculator实现或您在测试中使用的模拟。

答案 2 :(得分:1)

这取决于你改变范围的方法。 单元 是一个软件中最小的可测试组件 - 它很少意味着每个方法一个测试。

我发现全面测试我的公共方法足以建立正确的行为。如果使用测试包装私有方法,您可能会发现测试开始限制代码开发。

如果您的程序更具程序性,您可能会发现需要在您描述的粒度级别进行测试,在这种情况下使用友元组件是一个很好的解决方案。但是,我建议您很少需要测试非公开的方法。

答案 3 :(得分:1)

太少的价值工​​作太多了。所有测试告诉我(如果它通过)是调用MyMethod调用另一个私有方法。单元测试应该测试MyMethod()提供的行为 - 调用MyMethod后会发生什么/改变什么?

问题的标题也有点误导 - 我没有看到与依赖相关的问题。

大多数情况下你不需要InternalsVisibleTo ..只需通过运行它的公共方法测试私有方法,例如为MyMethod()编写单元测试。此外,如果您练习测试优先编程,那么您已经拥有涵盖私有方法中每行代码的测试。

答案 4 :(得分:0)

如果这是一段您不敢触及的遗留代码,我建议您创建一个构建MyClass的单​​元测试。暂时在MyClass中创建一个公共属性以显示x的值。

在单元测试中刚刚创建断言,在实例化MyClass之后x的值为100。一旦你有了这个,像@alb建议的重构。再次运行测试,确保x仍为100并单独测试计算器类,最终删除MyClass中x的暂定公共属性。希望有所帮助。