简体   繁体   中英

Exception not thrown by mocked object

Objective is to unit test a PUBLIC VOID Method.

I have a mocked service, which my class-under-test would call, in a for-each loop with 3 different parameters.

The class-under-test passes some input parameters to "SendRequest" method of the service which executes using those parameters.

I want the mocked service to throw an exception when one of the parameters has a specific value eg "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. 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).

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. But I never get to get inside the Catch block.

What am I doing wrong?

Option 1: Specific Exception
My 1st suggestion would throw a more specific exception so you can be more sure.

Option 2: Inject an ILogger service
Refactor out the logging into an ILogger and inject that in. Then pass a mock of that in and assert against it.

Option 3: Extract and Override
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/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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