使用其他方法的方法的单元测试

时间:2012-09-07 13:20:51

标签: c# unit-testing

我是单元测试的新手,并且混淆了如何写单元测试方法:

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())没有运行。我读到了存根,并认为在这种情况下我应该使用存根,但它是如何做到这一点令人困惑。 你能发一些关于存根的简单例子。

3 个答案:

答案 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);

...