我有以下课程:
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()
方法。有没有办法测试方法是否被调用?
或者也许是关于如何对我的案例进行单元测试的另一个想法?
感谢。
答案 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的现有调用,但它们现在更易于替换和测试。
这不是一个完美的解决方案,但是,如果维护遗留代码,它可以让您添加一些可测试性而无需进行重大返工。
在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)
答案 4 :(得分:0)
取决于你的模拟框架。没有任何免费的(Rhino,Moq)允许您设置静态方法的期望。您需要购买一个商业产品,如Typemock或更好的仍然使ErrorDisplay类上的方法虚拟,并将其实例传递给MyClass。
作为一个例子,如果使用Moq,您可以发送Mock<ErrorDisplay>
的实例并设置期望值。