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.