3

I just started writing some C# function code these days and I have to send track event using Application Insight(AI). Here is the sample code that I wrote.

namespace BlobTrigger {
    public static class Main {
        private static string sKey = TelemetryConfiguration.Active.InstrumentationKey = System.Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY", EnvironmentVariableTarget.Process);
        private static TelemetryClient sTelemetry;
       [FunctionName("BlobTrigger")]
       public static void Run(
            [BlobTrigger("upload/{name}.wav")] Stream myBlob,
            string name,
            Microsoft.Azure.WebJobs.ExecutionContext context,
            TraceWriter log) {

            sTelemetry  = new TelemetryClient() { InstrumentationKey = sKey };
            sTelemetry.Context.Operation.Id = context.InvocationId.ToString();
            sTelemetry.Context.Operation.Name = name;
            sTelemetry.TrackEvent("File is uploaded");
            .....
    }
}

This function works fine. But my problem is writing some unit test for this. I create some mock class for four arguments of the Run method and overrode its method already. This was easy. but I don't know how to mock TelemetryClient#TrackEvent because I NEW that instance in the Run method.

I saw the page below using DI for this but I couldn't understand that how to write unit test properly.

Using Application Insights with Unit Tests?

So can you show me the example unit test code for this?

Steven
  • 166,672
  • 24
  • 332
  • 435
Capotasto
  • 131
  • 3
  • 17
  • 2
    What about refactoring the logic you actually want to test to a separate method or class? Azure Functions is just a port into your logic. It's the actual logic you want to test. – Wouter de Kort Mar 06 '18 at 18:40
  • @WouterdeKort Yes, I created several methods but these are all private method. So I can only access from test class is `Run` method. I don't want to send event during this unit test because the unit test will run every time it merged from other branches. – Capotasto Mar 06 '18 at 19:09
  • 1
    I'm not sure what you mean by sending events. I would recommend reading the book https://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052/ That book teaches you how to find the seams in your code where you can isolate behavior and just test what's important. The fact that your code is in private methods is good, but what about extracting those private methods into their own class and passing in all needed info in a public method that can be tested? – Wouter de Kort Mar 06 '18 at 19:52
  • It is expensive to create a new instance of `TelemetryClient` for each log, so you should rather instantiate it once. I'm curious how you mock `TraceWriter` and `ExecutionContext`? – Mikhail Shilkov Mar 06 '18 at 20:34
  • @Mikhail Yes, I create `TelemetryClient` just one time when the Run method called. You can find the way to mock for `TraceWriter` and `ExecutionContext` below. https://devxp.blogspot.ca/2017/07/unit-testing-your-azure-functions.html – Capotasto Mar 06 '18 at 21:32
  • @Capotasto You should create it just once when your class is first loaded. `Run` can be called many times, and you'll pay the price on every function execution. – Mikhail Shilkov Mar 06 '18 at 21:33
  • @Mikhail You mean that I can reuse it? I thought the instance will be terminated by GC every time the function finished. – Capotasto Mar 06 '18 at 21:37
  • @WouterdeKort Thank you for sharing the book info I will read it later. "I'm not sure what you mean by sending events." -> I need to send an event to Application Insight each function like "Starting TaskA". But I don't want to do this during Test. So I think I need to mock `TelemetryClient#TrackEvent`. – Capotasto Mar 06 '18 at 21:44
  • @Capotasto The instance will be reused between subsequent requests until it's killed (e.g. due to inactivity). You can and should reuse resources between executions. – Mikhail Shilkov Mar 06 '18 at 21:50

1 Answers1

5

First of all, Azure Functions supports Application Insights out-of-the-box.

Azure Functions now has direct integration with Application Insights

Therefore, I wouldn't recommend you to directly implement TelemetryClient in your code. Instead, replace your TraceWriter parameter to ILogger to get benefits from Application Insights.

However, if you really want to use the TelemetryClient within your code, I would recommend creating a wrapper interface like ITelemetryClientWrapper, implementing it and injecting it through the dependency injection approach.

I've written a blog post about dependency injection around Azure Functions:

Azure Functions with IoC Container

justinyoo
  • 2,123
  • 1
  • 21
  • 24