Skip to content

Commit

Permalink
apply feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmrdavid committed May 17, 2024
1 parent fb8b654 commit 53360fd
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 11 deletions.
5 changes: 5 additions & 0 deletions samples/AzureFunctionsApp/AzureFunctionsApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ public static class AzureFunctionsApp
public static async Task<List<string>> RunOrchestrator(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
ILogger logger = context.CreateReplaySafeLogger(nameof(AzureFunctionsApp));
logger.LogInformation("Saying hello.");

var outputs = new List<string>();

// Replace name and input with values relevant for your Durable Functions Activity
Expand All @@ -29,6 +32,8 @@ public static async Task<List<string>> RunOrchestrator(
[Function(nameof(SayHello))]
public static string SayHello([ActivityTrigger] string name, FunctionContext executionContext)
{
ILogger logger = executionContext.GetLogger("SayHello");
logger.LogInformation($"Saying hello to {name}.");
return $"Hello {name}!";
}

Expand Down
49 changes: 38 additions & 11 deletions samples/AzureFunctionsUnitTests/SampleUnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace Company.Function; // same namespace as the Azure Functions app
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
using Moq.Protected;

public class SampleUnitTests
{
Expand All @@ -25,6 +26,14 @@ public async Task OrchestrationReturnsMultipleGreetings()
// create mock orchestration context, and mock ILogger.
Mock<TaskOrchestrationContext> contextMock = new();

// a simple ILogger that captures emitted logs in a list
TestLogger logger = new();

// The DurableTaskClient CreateReplaySafeLogger API obtains a logger from a protected LoggerFactory property, we mock it here
Mock<ILoggerFactory> loggerFactoryMock = new();
loggerFactoryMock.Setup(x => x.CreateLogger(It.IsAny<string>())).Returns(logger);
contextMock.Protected().Setup<ILoggerFactory>("LoggerFactory").Returns(loggerFactoryMock.Object);

// mock activity results
// In Moq, optional arguments need to be specified as well. We specify them with It.IsAny<T>(), where T is the type of the optional argument
contextMock.Setup(x => x.CallActivityAsync<string>(nameof(AzureFunctionsApp.SayHello), "Tokyo", It.IsAny<TaskOptions>()))
Expand All @@ -48,11 +57,29 @@ public async Task OrchestrationReturnsMultipleGreetings()
[Fact]
public void ActivityReturnsGreeting()
{
var contextMock = new Mock<FunctionContext>();
Mock<FunctionContext> contextMock = new();

// a simple ILogger that captures emitted logs in a list
TestLogger logger = new();

// Mock ILogger service, needed since an ILogger is created in the client via <FunctionContext>.GetLogger(...);
Mock<ILoggerFactory> loggerFactoryMock = new();
loggerFactoryMock.Setup(x => x.CreateLogger(It.IsAny<string>())).Returns(logger);
Mock<IServiceProvider> instanceServicesMock = new();
instanceServicesMock.Setup(x => x.GetService(typeof(ILoggerFactory))).Returns(loggerFactoryMock.Object);

// register mock'ed DI services
var instanceServices = instanceServicesMock.Object;
contextMock.Setup(x => x.InstanceServices).Returns(instanceServices);

var context = contextMock.Object;

string output = AzureFunctionsApp.SayHello("Tokyo", context);

// Assert expected logs are emitted
var capturedLogs = logger.CapturedLogs;
Assert.Contains(capturedLogs, log => log.Contains("Saying hello to Tokyo."));

// assert expected outputs
Assert.Equal("Hello Tokyo!", output);
}
Expand All @@ -66,15 +93,15 @@ public async Task ClientReturnsUrls()
// we need to mock the FunctionContext and provide it with two mocked services needed by the client
// (1) an ILogger service
// (2) an ObjectSerializer service,
var contextMock = new Mock<FunctionContext>();
Mock<FunctionContext> contextMock = new();

// a simple ILogger that captures emitted logs in a list
var logger = new TestLogger();
TestLogger logger = new();

// Mock ILogger service, needed since an ILogger is created in the client via <FunctionContext>.GetLogger(...);
var loggerFactoryMock = new Mock<ILoggerFactory>();
Mock<ILoggerFactory> loggerFactoryMock = new();
loggerFactoryMock.Setup(x => x.CreateLogger(It.IsAny<string>())).Returns(logger);
var instanceServicesMock = new Mock<IServiceProvider>();
Mock<IServiceProvider> instanceServicesMock = new();
instanceServicesMock.Setup(x => x.GetService(typeof(ILoggerFactory))).Returns(loggerFactoryMock.Object);

// mock JsonObjectSerializer service, used during HTTP response serialization
Expand All @@ -91,7 +118,7 @@ public async Task ClientReturnsUrls()
var context = contextMock.Object;

// Initialize mock'ed DurableTaskClient with the ability to start orchestrations
var clientMock = new Mock<DurableTaskClient>("test client");
Mock<DurableTaskClient> clientMock = new("test client");
clientMock.Setup(x => x.ScheduleNewOrchestrationInstanceAsync(nameof(AzureFunctionsApp),
It.IsAny<object>(),
It.IsAny<StartOrchestrationOptions>(),
Expand All @@ -100,7 +127,7 @@ public async Task ClientReturnsUrls()
var client = clientMock.Object;

// Create dummy request object
var request = new TestRequestData(context);
TestRequestData request = new(context);

// Invoke the function
var output = await AzureFunctionsApp.HttpStart(request, client, context);
Expand All @@ -111,7 +138,7 @@ public async Task ClientReturnsUrls()

// deserialize http output
output.Body.Seek(0, SeekOrigin.Begin);
using var reader = new StreamReader(output.Body, Encoding.UTF8);
using StreamReader reader = new(output.Body, Encoding.UTF8);
string content = reader.ReadToEnd();
Dictionary<string, string>? keyValuePairs = JsonSerializer.Deserialize<Dictionary<string, string>>(content);

Expand All @@ -136,11 +163,11 @@ public TestRequestData(FunctionContext functionContext) : base(functionContext)

public override Stream Body => new MemoryStream();

public override HttpHeadersCollection Headers => new HttpHeadersCollection();
public override HttpHeadersCollection Headers => new();

public override IReadOnlyCollection<IHttpCookie> Cookies => new List<IHttpCookie>();

public override Uri Url => new Uri("http://localhost:8888/myUrl");
public override Uri Url => new("http://localhost:8888/myUrl");

public override IEnumerable<ClaimsIdentity> Identities => Enumerable.Empty<ClaimsIdentity>();

Expand All @@ -160,7 +187,7 @@ public TestResponse(FunctionContext functionContext) : base(functionContext)
}

public override HttpStatusCode StatusCode { get; set; }
public override HttpHeadersCollection Headers { get; set; } = new HttpHeadersCollection();
public override HttpHeadersCollection Headers { get; set; } = new();

public override HttpCookies Cookies => throw new NotImplementedException();

Expand Down

0 comments on commit 53360fd

Please sign in to comment.