我是单元测试的新手,并且混淆了如何写单元测试方法:
public Boolean BeepInTime(Interfaces.IDateTime time,TimeSpan beepTime)
{
Interfaces.IBeep beep= new Beep();
var h = time.GetTime();
if (h == beepTime)
{
return beep.Beeping();
}
else
{
return false;
}
}
public Boolean Beeping()
{
try
{
SystemSounds.Beep.Play();
return true;
}
catch
{
return false;
}
}
在测试BeepInTime
时,我希望(beep.Beeping()
)没有运行。我读到了存根,并认为在这种情况下我应该使用存根,但它是如何做到这一点令人困惑。
你能发一些关于存根的简单例子。
答案 0 :(得分:4)
解决方案是 - 依赖注入。
每次执行或看到... = new ...();
(构造)时都会想到依赖注入,基本上显式实例化会引入依赖关系,然后有时为这样的代码编写单元测试变得微不足道。
因此,只需注入Beep
类而不是显式实例化IBeep
类。什么是好的 - 你已经有了一个抽象这个类的接口 - public Boolean BeepInTime(Interfaces.IBeep beeper, TimeSpan beepTime)
{
var h = time.GetTime();
if (h == beepTime)
{
return beep.Beeping();
}
return false;
}
,这将允许你在编写单元测试时创建并注入模拟而不是真正的类实例。
var mock = MockRepository.GenerateMock<IBeep>();
// setup Beeping() return value == true
mock.Expect(m => m.Beeping()).Return(true);
var isBeepInTime = BeepInTime(mock, timeSpan)
Ans想象一下这种技术在注入数据库访问服务或Web服务时是如何有用的,在编写单元测试时你不需要真正的数据库或Web服务 - 你只需要模拟数据库/网络服务就可以了。
使用RhinoMocks的示例:
{{1}}
答案 1 :(得分:3)
有很多不同的方法可以解决这个问题。最简单的方法是将Beep接口传递给类(通过构造函数或方法本身)。
public Boolean BeepInTime(Interfaces.IDateTime time,TimeSpan beepTime, Interfaces.IBeep beep)
{
var h = time.GetTime();
if (h == beepTime)
{
return beep.Beeping();
}
else
{
return false;
}
}
在单元测试中,您创建一个模拟IBeep对象并传入模拟。还有很多第三方模拟框架,所以你不必创建自己的模拟。我个人使用Moq,这是一个很好的。 RhinoMocks也很受欢迎。两者都是免费的。
如果您不想在生产代码中创建Beep类,那么您可以这样做......
public Boolean BeepInTime(Interfaces.IDateTime time,TimeSpan beepTime, Interfaces.IBeep beep = null)
{
if (beep == null)
beep = new Beep();
var h = time.GetTime();
if (h == beepTime)
{
return beep.Beeping();
}
else
{
return false;
}
}
注意:我通常不会将接口传递给构造函数并将其分配给字段,而是将其传递给方法,尤其是在从许多方法调用类的情况下)。还有很多其他方法可以做到这一点,包括使用NInject。我只是提供这些例子,让你先看看它是如何完成的。
我建议在决定采用任何一种方法之前,先在互联网上进行几天的研究,然后再进行研究。
答案 2 :(得分:2)
首先需要从Beep
方法中删除对BeepInTime
的依赖关系,如下所示:
public Boolean BeepInTime(Interfaces.IDateTime time,TimeSpan beepTime, Interfaces.IBeep beep)
{
var h = time.GetTime();
if (h == beepTime)
{
return beep.Beeping();
}
else
{
return false;
}
}
然后,当使用Moq:
之类的东西进行测试时,你可以模拟出IBeep的实现//Arrange
var mockBeep = new Mock<Interfaces.IBeep>();
mockBeep.Setup(b => b.Beeping())
.Returns(true);
// Act
var result = myClass.BeepInTime(myTime, myBeepTime, mockBeep.Object);
...