我使用Nunit进行单元测试,我需要对一个抛出异常的代码进行单元测试。我的代码与此类似。
public class Myclass
{
public int Count
{
get; set;
}
public void Foo()
{
try
{
if (Count >3)
{
throw new Exception();
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
}
[TestFixture]
public class TestMyClass
{
[Test]
public void TestFoo()
{
var obj = new Myclass();
obj.Count = 4;
Assert.Throws<Exception>(obj.Foo);
}
}
这会产生这样的错误
Expected: <System.Exception>
But was: null
我发现如果删除try catch块,单元测试就会通过。但我不想更改实际的代码。请告诉我如何对上述代码进行单元测试以及正确的方法。
答案 0 :(得分:3)
Lee's answer是正确的,但我希望通过实施它来扩展您获得的收益。
您的设计很好,但不测试友好。问题有两个:
MessageBox.Show
)MessageBox
)而不是抽象修复第二个问题会使代码可测试。你必须在单元测试中引入你将验证的假对象(正如李建议的那样)。正确修复第二个问题 (即通过接口注入依赖关系)也将解决第一个问题。
通过引入界面,我们做了两件事:
MessageBox
。实际的错误处理程序仍然可以一个消息框,但现在它也可以是自定义窗口,系统警报或日志消息。你的班级不需要知道它是什么,现在它没有(我们已经分开了关注点)。答案 1 :(得分:2)
由于您的方法吞没了所有异常,因此不能使用Assert.Throws
,因为不会抛出任何异常。
如果要检查是否以某种方式处理了异常,可以创建一个接口:
public interface IExceptionHandler
{
void Handle(Exception ex);
}
public void WinFormsExceptionHandler : IExceptionHandler
{
public void Handle(Exception ex)
{
MessageBox.Show(e.Message);
}
}
public class Myclass
{
private readonly IExceptionHandler handler;
public Myclass(IExceptionHandler handler) { this.handler = handler; }
public Myclass() : this(new WinFormsExceptionHandler()) { }
public int Count
{
get; set;
}
public void Foo()
{
try
{
if (Count >3)
{
throw new Exception();
}
}
catch (Exception e)
{
this.handler.Handle(e);
}
}
}
然后你可以在你的测试中使用模拟,例如使用你可以做的RhinoMocks:
[Test]
public void TestFoo()
{
var handler = MockRepository.GenerateMock<IExceptionHandler>();
var obj = new Myclass(handler);
obj.Count = 4;
obj.Foo();
handler.AssertWasCalled(h => h.Handle(Arg<Exception>.Is.Anything));
}