假设我有一个名为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对象是非常复杂的对象,很难创建对象,因为它们继承并包含许多其他依赖项。在这种情况下,
如何伪造CIRCLE和SQUARE对象? (注意:CIRCLE和SQUARE类没有Public Sub New,没有任何参数)
如何伪造CIRCLE和SQUARE对象的区域函数的结果(我只需要一个数字来测试我的ADD函数,我不关心如何计算区域)
< / LI> 醇>答案 0 :(得分:2)
如果您创建了CIRCLE
和SQUARE
类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
,它应该是Circle
和Square
而不是CIRCLE
和SQUARE
。此外,iMath
类名称不应以字母“i”开头。根据Microsoft标准,在类型名称的开头加上“I”意味着它是一个接口,而不是一个类。我看到的最后一个问题是你的ADD
函数没有指定返回类型。您应该始终为所有函数指定返回类型。例如,在我的示例中,我将其更改为返回As Integer
。
答案 1 :(得分:1)
您应该依赖现有的模拟框架,它正好解决了这个问题。我最喜欢的一个是NSubsitute。
它可以为具有虚函数的任何类接口创建模拟。一般而言,最好是创建手动模拟。