Using Application Insights with Unit Tests?

Microsoft.ApplicationInsights.TelemetryClient, which has no Interface and is a sealed class, with 2 constructors.

This TelemetryClient is a framework type and framework types should not be auto-wired by your container.

I found a post showing how to override the container's constructor resolution behavior, but that seems pretty complicated.

Yep, this complexity is deliberate, because we want to discourage people from creating components with multiple constructors, because this is an anti-pattern.

Instead of using auto-wiring, you can, as @qujck already pointed out, simply make the following registration:

container.Register<TelemetryClient>(() => 
    new TelemetryClient(/*whatever values you need*/),
    requestOrTransientLifestyle);

Or is Application Insights smart enough to know it is running in a unit test, and not send the data?

Very unlikely. If you want to test the class that depends on this TelemetryClient, you better use a fake implementation instead, to prevent your unit test to either become fragile, slow, or to pollute your Insight data. But even if testing isn't a concern, according to the Dependency Inversion Principle you should depend on (1) abstractions that are (2) defined by your own application. You fail both points when using the TelemetryClient.

What you should do instead is define one (or perhaps even multiple) abstractions over the TelemetryClient that are especially tailored for your application. So don't try to mimic the TelemetryClient's API with its possible 100 methods, but only define methods on the interface that your controller actually uses, and make them as simple as possible so you can make both the controller's code simpler -and- your unit tests simpler.

After you defined a good abstraction, you can create an adapter implementation that uses the TelemetryClient internally. I image you register this adapter as follows:

container.RegisterSingleton<ITelemetryLogger>(
    new TelemetryClientAdapter(new TelemetryClient(...)));

Here I assume that the TelemetryClient is thread-safe and can work as a singleton. Otherwise, you can do something like this:

container.RegisterSingleton<ITelemetryLogger>(
    new TelemetryClientAdapter(() => new TelemetryClient(...)));

Here the adapter is still a singleton, but is provided with a delegate that allows creation of the TelemetryClient. Another option is to let the adapter create (and perhaps dispose) the TelemetryClient internally. That would perhaps make the registration even simpler:

container.RegisterSingleton<ITelemetryLogger>(new TelemetryClientAdapter());

Application Insights has an example of unit testing the TelemetryClient by mocking TelemetryChannel.

TelemetryChannel implements ITelemetryChannel so is pretty easy to mock and inject. In this example you can log messages, and then collect them later from Items for assertions.

public class MockTelemetryChannel : ITelemetryChannel
{
    public IList<ITelemetry> Items
    {
        get;
        private set;
    }

    ...

    public void Send(ITelemetry item)
    {
        Items.Add(item);
    }
}

...

MockTelemetryChannel = new MockTelemetryChannel();

TelemetryConfiguration configuration = new TelemetryConfiguration
{
    TelemetryChannel = MockTelemetryChannel,
    InstrumentationKey = Guid.NewGuid().ToString()
};
configuration.TelemetryInitializers.Add(new OperationCorrelationTelemetryInitializer());

TelemetryClient telemetryClient = new TelemetryClient(configuration);

container.Register<TelemetryClient>(telemetryClient);

I had a lot of success with using Josh Rostad's article for writing my mock TelemetryChannel and injecting it into my tests. Here's the mock object:

public class MockTelemetryChannel : ITelemetryChannel
{
    public ConcurrentBag<ITelemetry> SentTelemtries = new ConcurrentBag<ITelemetry>();
    public bool IsFlushed { get; private set; }
    public bool? DeveloperMode { get; set; }
    public string EndpointAddress { get; set; }

    public void Send(ITelemetry item)
    {
        this.SentTelemtries.Add(item);
    }

    public void Flush()
    {
        this.IsFlushed = true;
    }

    public void Dispose()
    {

    }
}    

And then in my tests, a local method to spin-up the mock:

private TelemetryClient InitializeMockTelemetryChannel()
{
    // Application Insights TelemetryClient doesn't have an interface (and is sealed)
    // Spin -up our own homebrew mock object
    MockTelemetryChannel mockTelemetryChannel = new MockTelemetryChannel();
    TelemetryConfiguration mockTelemetryConfig = new TelemetryConfiguration
    {
        TelemetryChannel = mockTelemetryChannel,
        InstrumentationKey = Guid.NewGuid().ToString(),
    };

    TelemetryClient mockTelemetryClient = new TelemetryClient(mockTelemetryConfig);
    return mockTelemetryClient;
}

Finally, run the tests!

[TestMethod]
public void TestWidgetDoSomething()
{            
    //arrange
    TelemetryClient mockTelemetryClient = this.InitializeMockTelemetryChannel();
    MyWidget widget = new MyWidget(mockTelemetryClient);

    //act
    var result = widget.DoSomething();

    //assert
    Assert.IsTrue(result != null);
    Assert.IsTrue(result.IsSuccess);
}

Another option without going the abstraction route is to disable telemetry before doing running your tests:

TelemetryConfiguration.Active.DisableTelemetry = true;


If you don't want to go down the abstraction / wrapper path. In your tests you could simply direct the AppInsights endpoint to a mock lightweight http server (which is trivial in ASP.NET Core).

appInsightsSettings.json

    "ApplicationInsights": {
    "Endpoint": "http://localhost:8888/v2/track"
}

How to set up "TestServer" in ASP.NET Core http://josephwoodward.co.uk/2016/07/integration-testing-asp-net-core-middleware


Comments

  1. Ronan

    • 2018/2/8

    Using Application Insights with Unit Tests? Solution: Microsoft.ApplicationInsights.TelemetryClient, which has no Interface and is a sealed class, with 2 

  2. Carlos

    • 2017/6/26

    29. Application Insights has an example of unit testing the TelemetryClient by mocking TelemetryChannel. TelemetryChannel implements ITelemetryChannel so is pretty easy to mock and inject. In this example you can log messages, and then collect them later from Items for assertions.

  3. Aaron

    • 2017/12/28

    Microsoft.ApplicationInsights.TelemetryClient, which has no Interface and is a sealed class, with 2 constructors.

  4. Drake

    • 2016/8/22

    Unit Testing and Microsoft Application Insights. Josh Rolstad. Mar 30, 2016 · 3 min read. Microsoft recently released Application Insights into preview. This is a phenomenal tool that allows

  5. Carter

    • 2018/4/10

    You might be wondering how can we unit test for Application Insights. We have a good way to make it happens. Overview. We can stub the request of the 

  6. Tatum

    • 2018/10/22

    Or is Application Insights smart enough to know it is running in a unit test, and not send the data? Very unlikely. If you want to test the class that depends on this TelemetryClient , you better use a fake implementation instead, to prevent your unit test to either become fragile, slow, or to pollute your Insight data.

  7. Colombo

    • 2015/9/16

    The TelemetryClient seems to be well thought through and is easy to use for my purposes. While adding the client to my applications for 

  8. Jaiden

    • 2019/9/11

    Almost all my code is covered by unit tests. However, now that I've added some telemetry calls in some controllers, I'm having trouble setting up the dependencies. The telemetry calls are for sending metrics to the Microsoft Azure-hosted Application Insights service. The app is not running in Azure, just a server with ISS.

  9. Luka

    • 2019/5/11

    I previously had a unit test working however it does not seem to work with Microsoft.ApplicationInsights v2.5.1 using .NET 4.7.

  10. Simon

    • 2018/10/7

    Using Application Insights with Unit Tests? (4) I have an MVC web app, and I'm using Simple Injector for DI. Almost all my code is covered by unit tests. However, now that I've added some telemetry calls in some controllers, I'm having trouble setting up the dependencies.

  11. Talon

    • 2018/1/24

    I have an MVC web app, and I'm using Simple Injector for DI. Almost all my code is covered by unit tests. However, now that I've added some telemetry calls 

  12. Martin

    • 2018/6/3

    I could see telemetry being created when debugging, and from that perspective all the unit tests were green. However, they never seemed to leave the machine and reach the cloud. Something was amiss. I have been using Microsoft.ApplicationInsights and other related packages for a long time. However, Microsoft recently announced a new set of

  13. Jesiah

    • 2020/7/12

    I try to integrate the package applicationinsights to my application. My application is cover by unit test (mocha), but when I use a real 

  14. Brooks

    • 2021/2/7

    I could see telemetry being created when debugging, and from that perspective all the unit tests were green. However, they never seemed to leave 

  15. Parker

    • 2016/11/16

    I recently installed Application Insights on an Angular app via this tutorial. This was very helpful in getting things going, but it did not 

Comments are closed.

Recent Posts