单元测试:检查是否正在调用方法

时间:2011-10-14 10:04:51

标签: c# unit-testing

我有以下课程:

public class MyClass
{
    public void MyMethod()
    {
        try
        {
            ...
            save data to file
            ...
        }
        catch (Exception exception)
        { 
            ErrorDisplay.ShowErrorMessage("MyMethod", exception);
        }
    }
}

public class ErrorDisplay
{
    public static ShowErrorMessage(string methodName, Exception exception)
    {
        if (exception is IOException)
            MessageBox.Show(methodName + " : " + GetIODisplayMessage());
        else if ... 
            ...
        else
            ...
   }

    public static string GetIODisplayMessage()
    {
        return "IO error";
    }

    ....
}

我需要写一个单元测试,我会模拟IOException,我需要检查一下 正在调用GetIODisplayMessage()方法。有没有办法测试方法是否被调用? 或者也许是关于如何对我的案例进行单元测试的另一个想法?

感谢。

5 个答案:

答案 0 :(得分:3)

如上所述,使用静态ErrorDisplay会导致测试和注入IErrorDisplay实现中的一些问题会解决其中的一些但不是全部(请参阅下面的MessageBox)。然而...

静态+接口

如果你需要保留静态类ErrorDisplay并且不想为TypeMock发送shell,你可以添加一个间接级别

public interface IDisplayErrorImplementation {
    void ShowErrorMessage(string message, Exception ex);
}

public class DefaultDisplayErrorImplementation : IDisplayErrorImplementation {
    public void ShowErrorMessage(string message, Exception ex) {
        //...
    }
}

public static class DisplayError {
    static DisplayError(){
        Implementation = new  DefaultDisplayErrorImplementation();
     }

    public static IDisplayErrorImplementation Implementation { get; set;}

    public static void ShowErrorMessage(string message, Exception ex) {
        Implementation.ShowErrorMessage(message, ex);
    }
}

您可以保留对ErrorDisplay的现有调用,但它们现在更易于替换和测试。

这不是一个完美的解决方案,但是,如果维护遗留代码,它可以让您添加一些可测试性而无需进行重大返工。

的MessageBox

在ErrorDisplay上运行单元测试时遇到另一个问题;你会看到一个MessageBox。如果尝试在测试工具中运行测试(或作为构建的一部分),那就不好了。

再次间接是你的朋友。您可以通过调用MessageDisplayService.Show()来替换MessageBox.Show()调用。默认实现可以调用消息框,虚拟实现可以用于测试(无论是模拟还是简单都不执行)

单元测试

您的测试单位界限是什么?

从您想要针对MyClass :: MyMethod()运行测试的问题看,导致IOException并且看到调用了GetIODisplayMessage()。如果我误解了,请跳过下一节:)

您是否计划为可能发生IOException的每个类/方法执行此操作? 您是否计划为每个可以发生ErrorDisplay处理的其他异常类型的类/方法执行此操作?

这是一项很多工作,只需一次又一次地重新测试ErrorDisplay代码。

我会看到的界限是

<强> MyClass的::的MyMethod

如果发生异常,则调用传递“MyMethod”的ErrorDisplay.ShowErrorMessage()和异常。

之后的任何事情都不在其中,不应该成为单元测试的一部分。

<强> ErrorDisplay.ShowErrorMessage()

如果使用IOException调用它,则会通过调用GetIODisplayMessage()显示它获得的消息。

这是调用代码的独立(如上所述),可以单独进行单元测试。

<强> ErrorDisplay.GetIODisplayMessage()

返回正确的错误消息。好吧,如果值是硬编码但是显示原则有点过分。

在测试MyClass :: MyMethod时,我们要验证当发生异常时,调用错误显示代码传递正确的方法名称和异常。

在测试ErrorDisplay.ShowErrorMessage()时,我们验证我们为我们称之为MessageDisplayService.Show()的异常类型获取了正确的消息     methodname +“:”+&lt;&gt; 我们不需要在这里测试IODisplayMessage的文本。

在测试ErrorDisplay.GetIODisplayMessage()时,我们检查它是否返回正确的消息。

希望这很有用,

艾伦。

答案 1 :(得分:1)

您无法直接检查是否正在调用方法,但您可以将MyMethod中的主体提取到实现接口的单独类中,然后使用一些模拟库(如RhinoMocks)来注入行为。

使用RhinoMock,您可以指示mock抛出异常并期望调用。

答案 2 :(得分:0)

测试静态方法很难。您可以考虑将方法更改为非静态方法。如果使用依赖注入,这会容易得多。另一个选择是购买商业TypeMock库的许可证,它允许您通过修改IL代码来模拟静态方法。这允许您编写用于验证方法被调用的代码,参数是什么等等。

答案 3 :(得分:0)

你应该使用模拟库,如Moq或Rhino Mocks 看看这个Moq

对于静态方法,您可以使用TypeMock Isolator

但不是免费的

答案 4 :(得分:0)

取决于你的模拟框架。没有任何免费的(Rhino,Moq)允许您设置静态方法的期望。您需要购买一个商业产品,如Typemock或更好的仍然使ErrorDisplay类上的方法虚拟,并将其实例传递给MyClass。

作为一个例子,如果使用Moq,您可以发送Mock<ErrorDisplay>的实例并设置期望值。