I got a simple Azure function:
[FunctionName("TestFunction")]
public async Task Run([CosmosDBTrigger(
databaseName: "Test",
containerName: "test",
CreateLeaseContainerIfNotExists = true,
MaxItemsPerInvocation = 200,
FeedPollDelay = 10000,
Connection = "TestConnection",
LeaseContainerName = "testLeases")]IReadOnlyList<Document> inputs,
[EventHub("test", Connection = "TestConnectionString")] IAsyncCollector<EventData> outputEvents,
ILogger log)
{
if (inputs != null && inputs.Count > 0)
{
try
{
await inputs.ForEachAsync(dop: 10, input =>
{
var id = input.GetPropertyValue<string>("id");
var eventData = new EventData(Encoding.UTF8.GetBytes(input.ToString()));
return outputEvents.AddAsync(eventData, id);
});
}
catch (Exception exception)
{
_logger.LogError(exception, $"Error occurred while sending reports feed to datalake: {exception.Message}");
}
}
}
I'm trying to mock outputEvents and assert that AddAsync(eventData, id) has received a call. While it's no problem for AddAsync(eventData), it's a bit problematic for the above case, as I receive an exception from EventHubWebJobsExtensions:
public static Task AddAsync(this IAsyncCollector<EventData> instance, EventData eventData, string partitionKey, CancellationToken cancellationToken = default(CancellationToken)) =>
instance switch
{
EventHubAsyncCollector ehCollector => ehCollector.AddAsync(eventData, partitionKey, cancellationToken),
_ => throw new InvalidOperationException("Adding with a partition key is only available when using the Event Hubs extension package.")
};
For AddAsync(eventData) I simply use suggested approach:
public class MockAsyncCollector<T> : IAsyncCollector<T>
{
public readonly List<T> Items = new List<T>();
public Task AddAsync(T item, CancellationToken cancellationToken = default)
{
Items.Add(item);
return Task.FromResult(true);
}
public Task FlushAsync(CancellationToken cancellationToken = default)
{
return Task.FromResult(true);
}
}
Which works fine, but I cannot figure out the solution for AddAsync(eventData, id).
Currently my test looks like this:
public class Tests
{
private readonly MockLogger<Test> _logger;
private readonly Test _processor;
public Tests()
{
_logger = Substitute.For<MockLogger<Test>>();
_processor = new Test(_logger);
}
[Fact]
public async Task Run_GivenProperMessage_ShouldNotThrowAndCallAddAsync()
{
// Arrange
var input = new List<Document>();
var inputDocument = new Document();
inputDocument.SetPropertyValue("id", Guid.NewGuid().ToString());
input.Add(inputDocument);
var output = Substitute.For<IAsyncCollector<EventData>>();
// Act
var act = async () => await _processor.Run(input, output, _logger);
// Assert
await act.Should().NotThrowAsync();
await output.Received(1).AddAsync(Arg.Any<EventData>(), Arg.Any<string>());
}
}
Seems the reason for an exception is a switch, which expects EventHubAsyncCollector, instead it got an NSubstitute ObjectProxy.
The issue is due to the fact that the
IAsyncCollector<EventData>instance in your test is a substitute from NSubstitute, and theAddAsyncmethod is not being intercepted correctly due to the switch statement in theAddAsyncextension method.IAsyncCollector<EventData>that correctly implements theAddAsyncmethod with partition key support.Test:
Here is my custom implementation for testing purposes:
Sample data:
MyEventDatainstances) representing the transformed data from the Cosmos DB Trigger, and each event is associated with the correct partition key. The specific details of the expected result depend on the input documents given in the Azure Function.Produced Events: