0

我仍在尝试掌握单元测试的窍门,我有一个简单的问题。今天我想为一个非常简单的函数写一个测试。这个函数就是这样做的:

void OnSomething()
{
   increment++;

   if (increment == 20)
     SaveIt();
}

我说,这个功能是可以测试的。我可以编写一个调用它 20 次的测试,然后验证 SaveIt 是否已被调用。

然后我的疑惑就出现了。如何测试 SaveIt 是否已被调用?我的第一个答案是添加一个布尔值,但后来我想:添加类功能只是为了使其可测试是否正确?

请指教。谢谢你。

4

2 回答 2

3

我建议让 SaveIt 返回一个成功或失败的结果,这只是让整体测试更容易。你可以做一些简单的事情,比如让它返回一个布尔值,或者如果你需要报告它是通过还是失败,你可以创建一个包含设置消息能力的通用结果类。

一个简单的例子

public class Result
{
   public bool IsSuccess;
   public List<string> Messages;
}

在单元测试中,您尝试仅测试 OnSomething 行为——不应测试“SaveIt”内部发生的情况。因此,理想情况下,您希望 SaveIt() 出现在另一个类中,以便您可以模拟它的响应。

我为此目的使用最小起订量。起订量是免费的,您可以在这里获取:http ://code.google.com/p/moq/

我的方法会变成

Result OnSomething()
{
    Result result=null;
    increment++;
    if(increment == 20)
    {
         result = saver.SaveIt();
    }
    return result;
}

您的类构造函数将采用一个实现 ISaver 接口的对象(定义 SaveIt() 方法)(理想情况下由 DI 框架注入,但如果必须,您可以手动生成它)。

现在在您的单元测试中,您将创建一个模拟版本的 ISaver 并告诉它在调用时返回什么:

 Mock<ISaver> mock = new Mock<ISaver>();
 mock.Setup(x=> x.SaveIt()).Returns(new Result{IsSuccess=true});

您将在构造函数 ISaver 参数中实例化传递 mock.Object 的类。

前任。

 MyClass myClass = new MyClass(mock.Object);  
 //(assuming it didn't have other parameters)

然后,您可以断言结果是否为空——如果它从未被调用,它将为空,因为您在上面所做的设置永远不会触发。

 (in nunit)
 Result result = myClass.OnSomething();
 Assert.IsNotNull(result);

如果您真的不希望 OnSomething() 返回结果,或者因为它是一个事件而不能返回结果,那么我会让 OnSomething() 调用一个方法来为您完成工作:

 void OnSomething()
 {
      Result result = DoTheWork();
 }

 Result DoTheWork()
 {
    Result result=null;
    increment++;
    if(increment == 20)
    {
         result = saver.SaveIt();
    }
    return result;
 }

然后在 DoTheWork() 而不是 OnSomething() 上运行单元测试。

于 2012-06-19T20:53:22.180 回答
2

当然不!生产代码根本不应该依赖于测试,但测试应该验证实际代码的正确行为。这可以通过多种方法来实现,例如IOC和使用mocks。你可以看看一些现有的框架,它们可以大大简化你的生活:

于 2012-06-19T21:24:20.177 回答