I'm trying to figure out a way to design xUnit tests for my Azure Functions, which are based on this example.
As you can see, with .net 5 functions, you are expected to start func host start in your functions' project directory, then you can fire functions and debug.
Here's the program.cs of the functions project
var host = new HostBuilder()
.ConfigureAppConfiguration(c =>
{
c.AddCommandLine(args);
})
.ConfigureFunctionsWorker((c, b) =>
{
b.UseMiddleware<FunctionExceptionMiddleware>(); //an extension method of mine to register middlewares...
b.UseFunctionExecutionMiddleware();
})
.ConfigureServices(s =>
{
//add other services here,
CloudStorageAccount storageAccount = CloudStorageAccount.Parse("");
BlobServiceClient blobServiceClient = new BlobServiceClient("");
s.AddScoped(r => storageAccount);
s.AddScoped(r => blobServiceClient);
})
.Build();
As you can see, I register different types for DI to work properly, and I also register a middleware, to handle unhandled exceptions, so testing only the function's class Run method isn't optimal, because they rely on DI and also I need the middleware(s) to be tested as well.
Here an example function
[FunctionName("GetAsset")]
public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequestData req, FunctionExecutionContext executionContext)
{
var response = new HttpResponseData(HttpStatusCode.OK);
var container = req.GetQueryStringParam("container"); //extension method to retrieve query parameters...
var blob = req.GetQueryStringParam("blob");
if (string.IsNullOrEmpty(container) || string.IsNullOrEmpty(blob))
{
executionContext.Logger.LogInformation($"GetAsset function - missing arguments in request string");
return new BadRequestObjectResult("");
}
return new OkObjectResult("Success");
}
As far as I can see, to get optimal tests, func.exe from the toolkit would need to be launched, then with an HttpClient I could make requests to http://localhost:7071/api/MyFunction.
I've thought of launching func.exe through Process.Start() before each of the tests, but it's kind of hackish as I don't really know when it is ready (func.exe compiles all assemblies before showing a "Worker process started and initialized" message) and tests could give false fails because of that.
Any ideas ?
Related
Ivé this code...
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "v1/Sala")]
HttpRequest req, [SignalR(HubName = "{query.HubName}")]
IAsyncCollector<SignalRMessage> signalRMessages) {
await signalRMessages.AddAsync(
new SignalRMessage {
Target = "newMessage",
Arguments = new[] { "Hello" }
});
}
And this code works good and I am happy. But I've a need, in partiular on my EventGridTrigger...
As you could noticed on the code above, the hubname is dinamic and an EventGridTrigger is a sort of special kind of endpoint (Your client app will not call and consume it...SignalR will instead).
But I am capable to identify the hubname on my EventGridTrigger...I can do this:
SignalRDataEvent data =
JsonConvert.DeserializeObject<SignalRDataEvent(eventGridEvent.Data.ToString());
string hubname = data.hubname;
But now...I need to send a signalR message using my variable hubname. Since I can't put [SignalR(HubName = "{query.HubName}")] IAsyncCollector signalRMessages) on my EventGridTrigger, I need to create the object SignalR, probably pass credentials, hubname, etc and then send a message. I can't find any sample for this - At least no samples that can work in serverless c# azure functions. Can someone help me wikth this ?
Try the following:
[SignalR(HubName = "{data.hubName}")]
Trying to write some integration tests for the first time in .NET - specifically for HotChocolate.
I’ve got had tests working with the WebApplicationFactory, the final part left is trying to now mock the authentication. I tried to set it up based on Mock Authentication but I’m querying the data with IRequestExecutor and finding the de-facto way of setting it up doesn't actually fire:
[Fact]
public async Task GetJokes()
{
var query =
#"query Jokes {
jokes(jokeLength: SMALL) {
nodes {
id
body
}
}
}";
var request = QueryRequestBuilder.New().SetQuery(query).Create();
var executor = await _factory.Services.GetRequestExecutorAsync();
var result = await executor.ExecuteAsync(request);
(await result.ToJsonAsync()).MatchSnapshot();
}
When I debug this I never hit HandleAuthenticateAsync in my AuthHandler’s (setup from the Mock Authentication linked above), so the claims are never added - so looks like those only run for the httpClient requests - is there a way to configure the IRequestExecutor to be authenticated too?
You can add the ClaimsPrincipal directly as property to the QueryRequestBuilder.
var request = QueryRequestBuilder
.New()
.SetQuery(query)
.AddProperty(nameof(ClaimsPrincipal), new ClaimsPrincipal(new ClaimsIdentity("test")))
.Create();
I've created some functions and have used this post https://www.rickvandenbosch.net/blog/azure-functions-binding-to-a-property/ to create them using custom properties
I set up the test project and files using the docs here https://learn.microsoft.com/en-us/azure/azure-functions/functions-test-a-function
My question is, how can functions that use property binding be unit tested? as all the examples I've looked at online use the test factory class to pass in a type of httpRequest.
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] GetStudyDetailsRequest req,
ILogger log)
{
try
{
var client = new ArtenaServiceClient(ArtenaServiceClient.EndpointConfiguration.BasicHttpBinding_IArtenaService);
var result = await client.GetStudyDetailsAsync(req.StudentID, req.Dev);
return CreateActionResult.Create(result);
}
catch (Exception e)
{
log.LogError(e, "", req);
return new OkObjectResult(new { e.Message, e.StackTrace });
}
}
I won't need to include Azure Function request property binding in my "unit" test scope firstly because that's not part of my code I am calling a unit here, secondly that's not my code at all :). That kind of testing might be relevant for end to end integration test where you would send http request to a running Function app. So, here in this case, I would just treat the function Run method as a regular C# method by passing required input directly from unit test.
We're using ASP.NET Core 2.2 to build a Web API project that effectively is a smart proxy for another API. We don't want to do anything special with access tokens though, and just forward them when calling the other API.
I want to avoid "sync over async" calls, especially in constructors and DI resolvers and factories, while still relying on the framework to handle retrieving access tokens from the current http request.
Here's a representative repro of our scenario:
public class TheOtherApi : ITheOtherApi
{
private readonly HttpClient _client;
public TheOtherApi(HttpClient client, IHttpContextAccessor httpContextAccessor)
{
_client = client;
// Yuck! Sync over async, inside a constructor!
var token = httpContextAccessor.HttpContext.GetTokenAsync("access_token").Result;
client.SetBearerToken(token);
}
public Task<string> ForwardSomethingCool(MyCommand command)
{
// E.g.
var response await _client.PostAsync(command.path, command.body);
return await response.Content.ReadAsStringAsync();
}
}
Registrations in Startup along these lines:
services.AddScoped<ITheOtherApi, TheOtherApi>();
services.AddHttpContextAccessor();
services.AddScoped(_=> new HttpClient { BaseAddress = new Uri("https://example.org/api") });
This perfectly demonstrates my problem: there's a .Result in there, that I want to get rid of entirely, without just moving it to some kind of factory function registered in Startup.
Searching for a synchronous way to get the access token, I went down into the source of GetTokenAsync(...) and see what other method I could use intead. I find that it in fact has all sorts of side-effects, for example it does AuthenticateAsync(...) before doing what the method name suggests ("getting a token" from the context).
I actually only want the logic from GetTokenValue(...) without the other bits, and hurray (!): it is not async.... but that method relies on AuthenticationProperties which I don't have readily available from the HttpContextAccessor?
My current workaround (or 'solution'?) is to do the work myself:
httpContextAccessor.HttpContext.Request.Headers
.TryGetValue("Authorization", out var authorizationHeader);
var bearerToken = authorizationHeader
.SingleOrDefault()
?.Replace("bearer ", "", StringComparison.InvariantCultureIgnoreCase);
if (string.IsNullOrWhiteSpace(bearerToken))
{
throw new ArgumentException(nameof(httpContextAccessor), "HttpContextAccessor resulted in no Access Token");
}
_client.SetBearerToken(token);
How could I synchronously get the access_token from a IHttpContextAccessor without writing my own entire headers/string manipulation helper function to extract it from the Headers?
I'm trying to log every request made to Nancy in the application pipeline, but for some reason I cannot seem to log the static file requests. Neither of the following captures the request for static files.
ApplicationPipelines.BeforeRequest += ((ctx) => {
LogTo.Info($"{ctx.Request.UserHostAddress} - {ctx.Request.Method} {ctx.Request.Path}");
return null;
});
ApplicationPipelines.AfterRequest += ((ctx) => {
LogTo.Info($"{ctx.Request.UserHostAddress} - {ctx.Request.Method} {ctx.Request.Path} - {ctx.Response.StatusCode}");
});
Anything that isn't a static file is logged perfectly fine.
Also, I'm using the convention located here: https://stackoverflow.com/a/21477824/1657476. Nancy is Self-Hosted using Nowin and is the only component added to the owin app builder.
Is the static file hander not part of the Nancy application pipeline? If so, How can I hook into the static file requests?
Documentation says it should be (part of the pipeline). So I don't understand why I can't pick it up. I've even tried directly inserting the pipeline hook at the beginning too with AddItemToStartOfPipeline
The static file handler is no longer part of the Nancy pipeline. Documentation was out of date. See Issue: https://github.com/NancyFx/Nancy/issues/2328
If you use Nancy with Owin you can use middleware to capture the request and response (equivalent to AfterRequest):
app.Use(new Func<AppFunc, AppFunc>(next => (async context =>
{
var sendingHeaders = (Action<Action<object>, object>) context["server.OnSendingHeaders"];
sendingHeaders(state =>
{
var ip = context["server.RemoteIpAddress"];
var port = context["server.RemotePort"];
var method = context["owin.RequestMethod"];
var path = context["owin.RequestPath"];
var status = context["owin.ResponseStatusCode"];
LogTo.Info($"{ip}:{port} - {method} {path} - {status}");
}, context);
await next.Invoke(context);
})));
You need the OnSendingHeaders hook because Nancy does not pass through if it handles the request.