未被模拟对象抛出的异常

本文关键字:异常 对象 模拟 | 更新日期: 2023-09-27 18:17:00

目标是单元测试PUBLIC VOID方法。

我有一个mock服务,我的class-under-test将在for-each循环中使用3个不同的参数调用它。

被测试类将一些输入参数传递给使用这些参数执行的服务的"SendRequest"方法。

我希望被模拟的服务在其中一个参数具有特定值时抛出异常,例如"abc"。我使用如下代码:

   public class ClassUnderTest
   {
       private IMyService _myservice;
       public ClassUnderTest(IMyService myservice)
       {
           _myservice = myservice;
       }
       public void MyMethod()
       {
           //assume I get those 3 values from somewhere, in here.
           var list = new List<string>{"abc","aaa","bbb"};
           foreach(var item in list)
               {
                   try
                   {
                       _myservice.SendRequest(item);
                   }
                   catch(Exception ex)
                   {
                       //do some logging and continue calling service with next item in list
                   }
              }
       }
   }
        var myService = new Mock<IMyService>();
        myService.Setup(x => x.SendRequest("abc")).Throws<Exception>();
        myService.Setup(x => x.SendRequest("aaa"));
        myService.Setup(x => x.SendRequest("bbb"));
        var classUnderTest = new ClassUnderTest(myService.Object);
        classUnderTest.MyMethod();
        myService.Verify(x =>x.SendRequest(It.IsAny<string>()), Times.Exactly(2));

更多上下文:由于MyMethod返回void,为了测试它,我只能依赖于在该方法中代码的不同部分调用我的依赖项这一事实。例如,如果在服务调用之前对输入参数进行空检查,则该方法将在调用服务之前返回。如果它通过了null检查,则将调用依赖项服务。我将能够在代码覆盖结果中(以及在调试模式下)跟踪这些。

当我运行测试时,它失败了,因为它调用了三次服务,但我期望调用发生两次(现在可能是我错了,可能是,虽然它应该抛出异常,调用尝试仍然被Verify调用计数,因此我得到3次运行)。

无论如何,在调试中我看到服务从来没有抛出异常。我在for-each循环中有一个try-catch,我想在其中做一些日志记录,并使用下一个值再次继续调用服务。但是我从来没有进入Catch块。

我做错了什么?

未被模拟对象抛出的异常

选项1:特定例外
我的第一个建议是抛出一个更具体的异常,这样你可以更确定。

选项2:注入ILogger服务
将日志重构到一个iloglogger中,并将其注入。然后传入一个mock,并对它进行断言。

选项3:提取和覆盖
如果必须检查catch块是否被击中,可以使用extract和override:

public class ClassUnderTest
{
    private IMyService _myservice;
    public ClassUnderTest(IMyService myservice)
    {
        _myservice = myservice;
    }
    public void MyMethod()
    {
        //assume I get those 3 values from somewhere, in here.
        var list = new List<string>{"abc","aaa","bbb"};
        foreach(var item in list)
            {
                try
                {
                    _myservice.SendRequest(item);
                }
                catch(Exception ex)
                {
                    LogError(ex);
                }
            }
    }
    protected virtual LogException(Exception ex)
    {
        //do logging
    }
}
public class TestableClassUnderTest : ClassUnderTest
{
    public bool LoggingWasCalled { get; set; }
    protected virtual LogException(Exception ex)
    {
        LoggingWasCalled = true;
    }
}

然后你可以像这样:

var testableSut = new TestableClassUnderTest ();
testableSut.MyMethod("abc");
Assert.True(testableSut.LoggingWasCalled);

我在这里更详细地介绍一下:http://devonburriss.me/testing-the-untestable/