用Nunit测试异常

本文关键字:异常 测试 Nunit | 更新日期: 2023-09-27 18:09:59

我使用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块,单元测试通过。但是我不想改变实际的代码。请告诉我如何单元测试上面的代码和做它的正确方法。

用Nunit测试异常

Lee的答案是正确的,但是我想详细说明通过实现它可以获得什么。

你的设计很好,但是不适合测试。这个问题是双重的:

  1. 将类依赖隐藏在静态方法调用(MessageBox.Show)后面
  2. 您依赖于具体实现(MessageBox)而不是抽象

修复第二个问题将使代码可测试。您必须引入假对象,以便在单元测试中进行验证(正如Lee建议的那样)。正确修复第二个问题(即通过接口注入依赖)也将修复第一个问题。

通过引入接口,我们完成了两件事:

  1. 抽象使用消息框通用错误处理程序组件——我们不再依赖于纯粹gui相关的MessageBox。实际的错误处理程序仍然可以是消息框,但现在它也可以是自定义窗口、系统警报或日志消息。你的类不需要知道它是什么,现在它不需要了(我们有分隔关注点)。
  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));
}