单元测试:检查一个方法是否被调用

本文关键字:方法 一个 是否 调用 检查 单元测试 | 更新日期: 2023-09-27 18:11:27

我有以下类:

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()方法。是否有一种方法来测试一个方法是否被调用?或者关于如何为我的案例做单元测试的另一个想法?

谢谢。

单元测试:检查一个方法是否被调用

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

静态+接口

如果你需要保持静态类ErrorDisplay而不想为TypeMock买单,你可以添加一个间接级别

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的现有调用,但它们现在更易于替换和可测试。

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

<标题>弹出窗口h1> ErrorDisplay上运行单元测试有另一个问题;您将出现一个消息框。如果试图在测试工具中运行测试(或作为构建的一部分),则不是很好。

再次强调,间接是你的朋友。您可以将MessageBox.Show()调用替换为对MessageDisplayService.Show()的调用。默认实现可以调用消息框,虚拟实现可以用于测试(模拟或简单的什么都不做实现)

<标题> 单元测试

测试的单元边界是什么?

从问题中可以看出,你想要对MyClass::MyMethod()运行一个测试,引起一个IOException,并看到GetIODisplayMessage()被调用。如果我理解错了,请跳过下一节:)

你打算这样做的每一个类/方法在IOException可能发生?您是否计划对每个类/方法执行此操作,其中每个由ErrorDisplay处理的其他异常类型都可能发生?

这是大量的工作,只是重新测试ErrorDisplay代码一遍又一遍。

我想看到的边界是

MyClass: MyMethod

如果发生异常,它调用ErrorDisplay.ShowErrorMessage(),传入"MyMethod"和异常。

之后的任何内容都不在它的控制范围内,不应该成为单元测试的一部分。

ErrorDisplay.ShowErrorMessage ()

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

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

ErrorDisplay.GetIODisplayMessage ()

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

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

当测试ErrorDisplay.ShowErrorMessage()时,我们验证我们获得了调用MessageDisplayService.Show()的异常类型的正确消息Methodname + ":" + <>我们不需要测试IODisplayMessage的文本。

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

希望这是有用的,

Alan。

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

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

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

取决于您的mock框架。没有一个免费的(Rhino, Moq)允许你对静态方法设置期望。你需要购买一个商业产品,比如Typemock,或者最好还是让ErrorDisplay类上的方法是虚的,并把它的一个实例传递给MyClass。

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

您应该使用mock库,例如Moq或Rhino mock看这个Moq

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

但不是自由的