2

Let say I have a class called CIRCLE, and another called SQUARE. In each of those class, I have a public function called area. (to calculate the area of the circle, and the area of the square respectively.)

I have also a class iMath. and in this class, I have a function ADD

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

Now I want to Unit test the ADD function. Obviously, for this very easy problem, I can easily create a circle object and a square object to Unit test my ADD function. However, let assume that my Circle object and Square object are very complicated objects, very hard to create a object, because they inherit and contains many other dependencies. In such case,

  1. How can I fake the CIRCLE and SQUARE objects ? (note: the CIRCLE and SQUARE classes don't have Public Sub New without any parameter in it)

  2. How can I fake the results of the area functions the CIRCLE and SQUARE objects (I just need a number to test my ADD function, and I don't care how the areas are calculated)

4

2 回答 2

2

如果您创建CIRCLESQUARE类的所有成员Overridable,那么您可以继承它们并使用存根覆盖所有成员,例如:

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函数没有指定返回类型。您应该始终指定所有函数的返回类型。例如,在我的示例中,我将其更改为 return As Integer

于 2013-08-13T12:21:19.537 回答
1

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

它可以为任何具有虚函数的类的接口创建模拟。一般来说,创建手动模拟更可取。

于 2013-08-22T07:33:01.970 回答