Reputation: 287
I'm testing an endpoint. I need to figure out how to get the test to pass with my loggerMock. Here is how I currently have the test set up:
public void GetExceptionReportSessionData_Returns200OK()
{
//Arrange
var response = new RetrieveExceptionReportSessionDatesResponse
{
RetrieveExceptionReportSessionDatesResult = string.Empty
};
var serviceClient = new Mock<WorkflowService.WorkflowService>();
serviceClient
.Setup(x => x.RetrieveExceptionReportSessionDatesAsync(It.IsAny<RetrieveExceptionReportSessionDatesRequest>()))
.ReturnsAsync(response);
var loggerMock = new Mock<ILogger>();
loggerMock.Setup(x => x.LogInfo(null));
var controller = new ExceptionReportController(loggerMock.Object);
var ctx = new ControllerContext() { HttpContext = new DefaultHttpContext() };
ctx.HttpContext.Request.Headers["token"] = "fake_token_here"; //Set header
controller.ControllerContext = ctx;
//Act
var result = controller.GetExceptionReportSessionData();
//Assert
var viewResult = Assert.IsType<OkObjectResult>(result);
Assert.Equal(StatusCodes.Status200OK, viewResult.StatusCode);
}
Here is how the logger is setup in the endpoint when returning a 200:
if (result != null && result.ExceptionReportLines != null && result.ExceptionReportLines.Count > 0)
{
logText = LogFormatter.Format(
WebUtilities.GetUser((ClaimsIdentity)HttpContext.User.Identity),
startTime, DateTime.Now, Privilege.ViewOrderExceptionReport,
"Get Exception Report", "Exception Report retrieved successfully.");
logger.LogInfo(logText);
}
else
{
logText = LogFormatter.Format
(WebUtilities.GetUser((ClaimsIdentity)HttpContext.User.Identity),
startTime, DateTime.Now, Privilege.ViewOrderExceptionReport,
"Get Exception Report", "Exception report is empty for the given report filters.");
logger.LogWarn(logText);
}
return Ok(result);
My test is set up so that the latter message appears. How can I get the test to pass?
Upvotes: 10
Views: 23780
Reputation: 2880
If you are using LogError
, LogDebug
, LogWarning
etc methods in .NET those methods are extension methods and now the issue is that you can't mock an extension method. So what you need to do is mock the underlying method which is actually called when you call LogError
, LogDebug
, LogWarning
etc methods.
Actually, all of those methods calls Log
method so you need to mock Log
method.
Definition of Log
method as below
void Log (this ILogger logger, LogLevel logLevel, EventId eventId, Exception exception, string message, params object[] args)
You can mock Log
method with LogWarning
extension method as below
[Fact]
public void VerifyLogWarning()
{
// Arrange
var loggerMock = new Mock<ILogger<MyClass>>();
var myclass = new MyClass(loggerMock.Object);
// Act
myclass.TestMethod();
// Assert
loggerMock.Verify(
x => x.Log(
It.Is<LogLevel>(l => l == LogLevel.Warning),
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => true),
It.IsAny<Exception>(),
It.Is<Func<It.IsAnyType, Exception, string>>((v, t) => true)), Times.Once);
}
In the above code, we are verifying whether we have called LogWarning
once as a part of TestMethod()
call.
You can also verify log warning message as below
// Assert
loggerMock.Verify(
x => x.Log(
It.Is<LogLevel>(l => l == LogLevel.Warning),
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString() == "LogWarning Message......"),
It.IsAny<Exception>(),
It.Is<Func<It.IsAnyType, Exception, string>>((v, t) => true)), Times.Once);
Upvotes: 25
Reputation: 23228
Instead of passing just a null
value to Setup
of LogInfo
method you can use an expression to match the logText
string
loggerMock
.Setup(x => x.LogInfo(It.Is<string>(s => s.Contains("Exception Report retrieved successfully."))))
.Verifiable();
And use Verify()
in Assert
step
loggerMock.Verify();
It ensures, that LogInfo()
method in loggerMock
was called with string matches the specified expression. Have a look at matching arguments in Moq
wiki for more details
Upvotes: 3