ApplicationInsights-dotnet
ApplicationInsights-dotnet copied to clipboard
Add interface on TelemetryClient
I wish TelemetryClient implemented an interface so that it would be easier to switch the client in automated tests
We would typically initialize TelemetryClient with the test channel like this.
Which methods would you prefer to have in interface - high level like TrackException
or lower level like Track
that accepts ITelemetryChannel
. Do you plan to test telemetry or just replace actual client with the one that does nothing?
I have both use cases: replacing the client to avoid a network dependency and verify the telemetry
It looks like StubTelemetryChannel solves both cases. Then I am happy and don't really need the interface.
@ogborstad I'm glad it is working for you. We still thinking what would be the best way to enable easy testing of telemetry client and use it with the DI. Thanks for asking!
@SergeyKanzhelev Is there any update here? Did the team decide not have an interface at all?
I believe having an Interface can help easily mock (use other advanced features) that most of the testing frameworks like Moq provide. Creating a StubTelemetryChannel
is helpful but I think we can do better
@GiriB for now we will proceed with the mock channel. Since there is no reason TelemetryClient
will ever be used as an interface in production code - defining it will cause confusion.
@SergeyKanzhelev what sort of confusion are you referring to? My team would very much benefit from having an interface for TelemetryClient so that we can use Moq to verify we are passing the correct dimensions to a metric. Yes, it is possible to do this today without Moq, but it is much more code than it needs to be.
@SergeyKanzhelev Your example of the stubbed telemetry channel involves using the TelemetryClient's constructor which is marked obsolete. Would be nice to have a mocked instance of TelemetryClient without all the compiler warnings that come with it today.
@SergeyKanzhelev Yes, please reconsider. ^^^
Simply asking that the TelemetryClient implement an ITelemetryClient so we can verify the TrackMetric calls.
That is the right level for us to verify. Our prod code knows nothing whatsoever about ITelemetryChannels, it would be weird if its unit tests had to. Doing so in effect makes the TelemetryClient part of the unit being unit tested, and that is of no interest to us.
Is there any updates on this? It seems to me that ITelemetryClient
would be a very straightforward implementation and would simplify many use cases for developers, just like we have ILogger
.
@TimothyMothra @cijothomas @lmolkova @SergeyKanzhelev could one of you reopen this issue please?
The TelemetryClient should support an interface to make it nice and easy to mock out.
I never yet wrote a unit test that wanted to cover the App Insights SDK code.
I'll reopen the issue for further consideration. Unfortunately, there are no work planned in this area in near term, so no guaranteed when or if this will be addressed. Azure monitor/Application Insights' overall direction is alignment with OpenTelemetry, so this can be discussed more when ApplicationInsights/OpenTelemetry integrations starts shipping ~towards end of 2020
cc: @rajkumar-rangaraj as Raj will be working on ApplicationInsights/OpenTelemetry integrations.
Do you have any update to share about this issue ? has it been shipped in any version (released or preview) ?
No update. No work is done on changing this so far. (This is unlikely to come soon, as the long term plan is to align with OpenTelemetry)
Why is it so difficult to add an interface and make a lot of developers happy? For me, I just want to test if the method TrackEvent of TelemetryClient is called.
This item is open for almost 5 years and still actual. Please implement ITelemetryClient.
Can someone at least put a link to StubTelemetryChannel? It would make this a lot easier
https://github.com/microsoft/ApplicationInsights-dotnet/blob/develop/WEB/Src/TestFramework/Shared/StubTelemetryChannel.cs
Hi all,
I've written a small package that acts as an interface for TelemetryClient
. It just requires another registration line in your ServiceCollection (services.AddApplicationInsightsTelemetryClientInterfaces()
)
Then you can inject an ITelemetryClient
into your class. Or more specific ones like ITelemetryEventLogger
or ITelemetryTraceLogger
.
Enjoy.
Github Link: https://github.com/thomhurst/ApplicationInsights.TelemetryLogger
NuGet:
Install-Package TomLonghurst.ApplicationInsights.TelemetryLogger
Where is this feature? We just need to verify TrackEvent() was called but can't do this currently
@JackSinclairT check out https://github.com/thomhurst/ApplicationInsights.TelemetryLogger
. Since there is no reason
TelemetryClient
will ever be used as an interface in production code - defining it will cause confusion.
The real confusing part the funky stub and that we need this GitHub issue to find the stub code/tricks
Anyway, is a Pull Request accepted?
By the way, this is the 3rd most updated issue. See Issues sorted by upvote - and it seems that number 2 is already fixed - so it's 2
Adding yet another comment in support of this. This would be super useful! We just want to be able to verify that the right method from the TelemetryClient is called with the expected parameters. Don't really want to introduce a unofficial third party library to wrap it or need to code a custom wrapper, just for testing. Is this going to be re-considered please?
Found myself again in a position where I simply needed to mock a telemetry client to pass in a dependency to satisfy the constructor when the tests themselves couldn't care about the telemetry itself.
Beating the drum for this long-standing request to be solved officially.
Same here. It can't be that hard to add this Interface. It would make developers live much easier. Do we need wait five more years?
Instead of an interface, here's a complete example using a stub telemetry channel. I've kept everything in one file and eliminated some blank lines, just for exposition purposes.
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using System.Collections.Concurrent;
using Xunit;
namespace TestProject1
{
public class UnitTest1
{
private ConcurrentQueue<ITelemetry> TelemetryItems { get; } = new ConcurrentQueue<ITelemetry>();
[Fact]
public void Test1()
{
var telemetryClient = CreateMockTelemetryClient();
var componentUnderTest = new ComponentUnderTest(telemetryClient);
componentUnderTest.Test();
Assert.True(TelemetryItems.TryDequeue(out ITelemetry? first));
Assert.IsType<RequestTelemetry>(first);
Assert.Equal("ItWorks", ((RequestTelemetry)first!).Name);
}
private TelemetryClient CreateMockTelemetryClient()
{
var telemetryConfiguration = new TelemetryConfiguration
{
ConnectionString = "InstrumentationKey=" + Guid.NewGuid().ToString(),
TelemetryChannel = new StubTelemetryChannel(TelemetryItems.Enqueue)
};
// TODO: Add telemetry initializers and processors if/as necessary.
return new TelemetryClient(telemetryConfiguration);
}
}
/// <summary>
/// A telemetry channel that simply calls a delegate.
/// </summary>
internal sealed class StubTelemetryChannel : ITelemetryChannel
{
private readonly Action<ITelemetry> _onSend;
public StubTelemetryChannel(Action<ITelemetry> onSend) => _onSend = onSend ?? throw new ArgumentNullException(nameof(onSend));
public bool? DeveloperMode { get; set; }
public string EndpointAddress { get; set; } = "";
public void Dispose() { }
public void Flush() { }
public void Send(ITelemetry item) => _onSend(item);
}
internal class ComponentUnderTest
{
private TelemetryClient _telemetryClient;
public ComponentUnderTest(TelemetryClient telemetryClient) => _telemetryClient = telemetryClient;
public void Test()
{
using var operation = _telemetryClient.StartOperation<RequestTelemetry>("ItWorks");
// TODO: Body of test
}
}
}
The interesting parts here are:
- The StubTelemetryChannel which just invokes a delegate on Send.
- Setting up a mock TelemetryClient that uses that stub channel. The delegate enqueues each telemetry item to a ConcurrentQueue that's accessible to the unit test.
- Passing that mock TelemetryClient to the component under test. This could also be done via dependency injection if that's more suited to your environment.
Obviously, all of this can be customized as appropriate for your application and testing environment.
FYI There is an excellent pr here: https://github.com/microsoft/ApplicationInsights-dotnet/pull/2761
Unfortunately there is no (usefull) response why it isn't getting merged.