如何在VB.NET中伪造一个非常复杂的TDD对象

时间:2013-08-13 11:31:45

标签: vb.net mocking tdd

假设我有一个名为CIRCLE的类,另一个叫做SQUARE。 在每个类中,我都有一个名为area的公共函数。 (计算 圆的面积和正方形的面积。)

我还有一个类iMath。在这堂课上,我有一个ADD函数

    Public Function ADD(byval c as CIRCLE, byval s as SQUARE) 
       Return c.area() + s.area()
    End Function 

现在我想对ADD功能进行单元测试。 显然,对于这个非常简单的问题,我可以轻松地创建一个圆形对象和一个方形对象来单元测试我的ADD函数。然而, 让我们假设我的Circle对象和Square对象是非常复杂的对象,很难创建对象,因为它们继承并包含许多其他依赖项。在这种情况下,

  1. 如何伪造CIRCLE和SQUARE对象? (注意:CIRCLE和SQUARE类没有Public Sub New,没有任何参数)

  2. 如何伪造CIRCLE和SQUARE对象的区域函数的结果(我只需要一个数字来测试我的ADD函数,我不关心如何计算区域)

    < / LI>

2 个答案:

答案 0 :(得分:2)

如果您创建了CIRCLESQUAREOverridable的所有成员,那么您可以继承它们并覆盖所有具有存根的成员,例如:

Public Class Circle
    Public Overridable Function Area() As Integer
        ' Complicated logic
    End Function
End Class

Public Class MockCircle
    Inherits Circle

    Public Overrides Function Area() As Integer
        Return 10
    End Function
End Class

但是,如果原始CIRCLE类在其构造函数中需要一堆难以创建的参数,那么这对您没有任何帮助,因为仍然需要调用MockCircle类基础构造函数。

首选解决方案是使用接口。不要让ADD方法接受具体的CIRCLE对象,而是让它请求实现ICircle接口的对象:

Public Interface ICircle
    Function Area() As Integer
End Interface

Public Class Circle
    Implements ICircle

    Public Function Area() As Integer Implements ICircle.Area
        ' Complicated logic
    End Function
End Class

Public Class MockCircle
    Implements ICircle

    Public Function Area() As Integer Implements ICircle.Area
        Return 10
    End Function
End Class

Public Class MyMath
    Public Function Add(c As ICircle, s As ISquare) As Integer
        Return c.Area() + s.Area()
    End Function
End Class

现在,CIRCLE在构造函数中需要什么并不重要,因为你已经把它完全排除在等式之外。现在,您可以为ADD方法提供CIRCLE对象或MockCircle对象,它将以任何方式工作。 MockCircle对象不从CIRCLE继承,因此它不共享任何复杂的依赖项。这种方法实际上也有一个方便的名称。它被称为依赖注入(DI)。如果您要进行大量的单元测试,那么值得您花时间对DI进行一些研究。理想情况下,CIRCLE类的依赖关系也都可以通过接口完成,因此您可以通过为其依赖项提供所有模拟对象来轻松创建一个CIRCLE对象。

我觉得有义务提及代码的一些表面问题。为了与.NET框架和Microsoft标准保持一致,您应该将PascalCase用于所有方法和类名。例如,它应该是Add而不是ADD,它应该是CircleSquare而不是CIRCLESQUARE。此外,iMath类名称不应以字母“i”开头。根据Microsoft标准,在类型名称的开头加上“I”意味着它是一个接口,而不是一个类。我看到的最后一个问题是你的ADD函数没有指定返回类型。您应该始终为所有函数指定返回类型。例如,在我的示例中,我将其更改为返回As Integer

答案 1 :(得分:1)

您应该依赖现有的模拟框架,它正好解决了这个问题。我最喜欢的一个是NSubsitute

它可以为具有虚函数的任何类接口创建模拟。一般而言,最好是创建手动模拟。