简体   繁体   English

模拟对象未引发异常

[英]Exception not thrown by mocked object

Objective is to unit test a PUBLIC VOID Method. 目的是对PUBLIC VOID方法进行单元测试。

I have a mocked service, which my class-under-test would call, in a for-each loop with 3 different parameters. 我有一个模拟的服务,在3个不同的参数的for-each循环中,被测类将调用该服务。

The class-under-test passes some input parameters to "SendRequest" method of the service which executes using those parameters. 被测类将一些输入参数传递给使用这些参数执行的服务的“ SendRequest”方法。

I want the mocked service to throw an exception when one of the parameters has a specific value eg "abc". 我希望模拟服务在参数之一具有特定值(例如“ abc”)时引发异常。 I use something like this: 我用这样的东西:

   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));

More Context: As MyMethod returns void, to test it, I can only rely on the the fact that my dependencies were invoked at different portions of the code in this method. 更多上下文:当MyMethod返回void进行测试时,我只能依靠这样的事实,即我的依赖项是在此方法的代码的不同部分调用的。 For eg if there is a null check for input parameters before service call, the method would return before it invokes the service. 例如,如果在服务调用之前对输入参数进行了空检查,则该方法将在调用服务之前返回。 If it goes past null check, the dependency service would be invoked. 如果通过空检查,则将调用依赖项服务。 I would be able to trace these in the code coverage results( and in debug mode). 我将能够在代码覆盖率结果(以及在调试模式下)中跟踪这些信息。

When I run the test, it fails because its invoking the service thrice but I expect the invocation to happen twice(now may be I am wrong and may be that although it is supposed to throw exception the invocation attempt is still counted by Verify call and hence I get 3 runs). 当我运行测试时,它由于调用服务三次而失败,但是我希望调用发生两次(现在可能是我错了,可能是尽管应该抛出异常,但调用尝试仍由Verify调用和因此我得到3次跑步)。

Whatever be the case, on debug I see that the service never throws exception. 无论如何,在调试时我看到该服务永远不会引发异常。 I have a try-catch in the for-each-loop where I want to do some logging and continue calling the service again with the next value. 我在for-each-loop中有一个try-catch,我想在其中进行一些日志记录,并继续使用下一个值再次调用该服务。 But I never get to get inside the Catch block. 但是我从来没有进入Catch块。

What am I doing wrong? 我究竟做错了什么?

Option 1: Specific Exception 选项1:特定例外
My 1st suggestion would throw a more specific exception so you can be more sure. 我的第一个建议将引发更具体的异常,因此您可以确定。

Option 2: Inject an ILogger service 选项2:注入ILogger服务
Refactor out the logging into an ILogger and inject that in. Then pass a mock of that in and assert against it. 将日志重构为ILogger并将其注入。然后传递一个模拟,并对其进行断言。

Option 3: Extract and Override 选项3:提取并覆盖
If you must check catch block was hit you can use extract and 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;
    }
}

Then you could sheck something like this: 然后,您可以添加以下内容:

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

I walk through it in more detail here: http://devonburriss.me/testing-the-untestable/ 我在这里更详细地介绍了它: http : //devonburriss.me/testing-the-untestable/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM