I have an Azure function which basically gets invoked upon HttpRequest to the endpoint. This function then makes calls to relevant sections in the database based on CREATE or UPDATE message that is passed in the payload.
public class InboundEvent
{
private readonly Func<MessageType, IMessageProcessor> _serviceProvider;
private readonly IAccessTokenValidator _accessTokenValidator;
public InboundEvent(Func<MessageType, IMessageProcessor> serviceProvider, IAccessTokenValidator accessTokenValidator)
{
_serviceProvider = serviceProvider;
_accessTokenValidator = accessTokenValidator;
}
[FunctionName("InboundEvent")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "abc/input")] HttpRequest req,
ILogger log)
{
try
{
await _accessTokenValidator.ValidateToken(req);
log?.LogInformation($"InboundMessageProcessor executed at: {DateTime.UtcNow}");
var request = await ProcessRequest(req);
await _serviceProvider(request.MessageType).ProcessMessage(request);
log?.LogInformation("InboundMessageProcessor function executed successfully.");
return new OkObjectResult("OK");
}
catch (Exception ex)
{
log?.Log(LogLevel.Error, ex, "Error");
return new InternalServerErrorResult();
}
}
private async Task<InputModel> ProcessRequest(HttpRequest req)
{
InputModel messageReq = new InputModel();
if (req.Headers != null && req.Headers.Any())
{
// form the InputModel datamodel object
// basically it can contain CREATE or UPDATE information
}
messageReq.Message = await new StreamReader(req.Body).ReadToEndAsync();
return messageReq;
}
}
Where the ServiceProvider component invokes MainBookingProcessor that takes appropriate action based on the Transaction type "CREATE" or "UPDATE"
public class MainBookingProcessor : IMessageProcessor
{
private readonly ICommandDispatcher _commandDispatcher;
public MainBookingProcessor(ICommandDispatcher commandDispatcher)
{
_commandDispatcher = commandDispatcher ?? throw new ArgumentNullException(nameof(commandDispatcher));
}
public async Task ProcessMessage(InputModel req)
{
switch (req.TransactionType)
{
case TransactionType.CREATE:
var command = new CreateBookingCommand()
{
System = req.System,
MessageId = req.MessageId,
Message = req.Message
};
await _commandDispatcher.SendAsync(command);
break;
case TransactionType.UPDATE:
var updateCommand = new UpdateBookingCommand()
{
System = req.System,
MessageId = req.MessageId,
Message = req.Message
};
await _commandDispatcher.SendAsync(updateCommand);
break;
default:
throw new KeyNotFoundException();
}
}
}
Now comes the main part of the issue that I'm facing. I'm writing a test component to test this Azure function using xUnit and Moq. For this I created an InboundEventTests class which will contain the test methods to test the Run method of the InboundEvent Azure function
public class InboundEventTests : FunctionTest
{
private InboundEvent _sut;
private readonly Mock<IMessageProcessor> messageProcessorMock
= new Mock<IMessageProcessor>();
private readonly Mock<Func<MessageType, IMessageProcessor>> _serviceProviderMock
= new Mock<Func<MessageType, IMessageProcessor>>();
private readonly Mock<IAccessTokenValidator> _accessTokenValidator
= new Mock<IAccessTokenValidator>();
private readonly Mock<ILogger> _loggerMock = new Mock<ILogger>();
private HttpContext httpContextMock;
private HeaderDictionary _headers;
private Mock<InputModel> inputModelMock = new Mock<InputModel>();
public InboundEventTests()
{
inputModelMock.SetupProperty(x => x.Message, It.IsAny<string>());
inputModelMock.SetupProperty(x => x.MessageId, It.IsAny<Guid>());
inputModelMock.SetupProperty(x => x.System, It.IsAny<string>());
}
public HttpRequest HttpRequestSetup(Dictionary<String, StringValues> query, string body)
{
var reqMock = new Mock<HttpRequest>();
reqMock.Setup(req => req.Headers).Returns(new HeaderDictionary(query));
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(body);
writer.Flush();
stream.Position = 0;
reqMock.Setup(req => req.Body).Returns(stream);
return reqMock.Object;
}
private HeaderDictionary CreateHeaders()
{
_headers = new HeaderDictionary();
_headers.TryAdd("MessageType","BOOKING");
_headers.TryAdd("TransactionType", "UPDATE");
_headers.TryAdd("MessageId", "some guid");
_headers.TryAdd("System", "NSCP_ORDER_MANAGEMENT");
return _headers;
}
[Fact]
public async Task RunFunctionTest()
{
//Arrange
var query = new Dictionary<String, StringValues>();
query.TryAdd("MessageType", "BOOKING");
query.TryAdd("TransactionType", "UPDATE");
query.TryAdd("System", "ORDER_MANAGEMENT");
query.TryAdd("MessageId", "some guid");
var body = JsonSerializer.Serialize(new {
Message = "BOOKING",
System = "ORDER_MANAGEMENT",
MessageId = "some guid"
});
The place I'm stuck is to create mocks for the delegate Func<MessageType,IMessageProcessor> which essentially routes to a specific class and transaction. How can I write the Mock stubs which is
so that i can pass those mock objects to my system under test and test for correctness if it was invoked correctly thereby sending Status.OK as result
_sut = new InboundEvent(_serviceProviderMock.Object, _accessTokenValidator.Object);
var result = await _sut.Run(req: HttpRequestSetup(query, body), _loggerMock.Object);
var resultObject = (OkObjectResult)result;
//Assert
Assert.Equal("OK", resultObject.Value);
Things I tried:
Creating a delegate mock using the syntax below, however
Mock<Func<MessageType, IMessageProcessor>> _serviceProviderMock = new Mock<Func<MessageType, IMessageProcessor>>();
_serviceProviderMock.Setup(_ => _(It.IsAny<MessageType>())).Returns(It.IsAny<IMessageProcessor>());
_sut = new InboundEvent(_serviceProviderMock.Object, _accessTokenValidator.Object);
var result = await _sut.Run(req: HttpRequestSetup(query, body), _loggerMock.Object);
But still the ProcessMessage in the InboundEvent class fails Object Reference Not set to an instance as the data is null.
question from:
https://stackoverflow.com/questions/65935724/writing-mocks-for-delegates-in-an-azure-function-using-moq