C# 将应用程序洞察与单元测试结合使用?
我有一个MVC web应用程序,我正在使用Simple Injector for DI。几乎我所有的代码都包含在单元测试中。然而,现在我在一些控制器中添加了一些遥测调用,设置依赖项时遇到了问题 遥测调用用于向Microsoft Azure托管的Application Insights服务发送度量。该应用程序没有在Azure中运行,只是一个带有ISS的服务器。AI门户告诉您有关应用程序的所有信息,包括使用遥测库发送的任何自定义事件。因此,控制器需要一个Microsoft.ApplicationInsights.TelemetryClient实例,该实例没有接口,是一个密封类,具有2个构造函数。我试着像这样注册它(混合生活方式与这个问题无关,我只是为了完整性而包括它):C# 将应用程序洞察与单元测试结合使用?,c#,unit-testing,azure,simple-injector,azure-application-insights,C#,Unit Testing,Azure,Simple Injector,Azure Application Insights,我有一个MVC web应用程序,我正在使用Simple Injector for DI。几乎我所有的代码都包含在单元测试中。然而,现在我在一些控制器中添加了一些遥测调用,设置依赖项时遇到了问题 遥测调用用于向Microsoft Azure托管的Application Insights服务发送度量。该应用程序没有在Azure中运行,只是一个带有ISS的服务器。AI门户告诉您有关应用程序的所有信息,包括使用遥测库发送的任何自定义事件。因此,控制器需要一个Microsoft.ApplicationIn
//优先于web api请求范围的混合生活方式
var requestOrTransientLifestyle=lifety.CreateHybrid(
()=>HttpContext.Current!=null,
新WebRequestLifestyle(),
生活方式(短暂的);
容器。注册(请求者TransientLifestyle);
问题在于,由于TelemetryClient有2个构造函数,SI会投诉并失败验证。我发现了一篇文章,展示了如何重写容器的构造函数解析行为,但这似乎相当复杂。首先,我想提出一个问题:
如果我不将遥测客户端作为注入依赖项(只需在类中创建一个新的依赖项),那么在每次运行单元测试时,遥测会被发送到Azure,从而创建大量虚假数据吗?或者Application Insights是否足够聪明,可以知道它正在单元测试中运行,而不发送数据
对此问题的任何“见解”都将不胜感激
谢谢
Microsoft.ApplicationInsights.TelemetryClient,它没有接口,是一个密封类,具有2个构造函数
此TelemetryClient
是一种框架类型,并且是
我发现了一篇文章,展示了如何重写容器的构造函数解析行为,但这似乎相当复杂
是的,这种复杂性是经过深思熟虑的,因为我们不想让人们创建具有多个构造函数的组件,因为这是非常复杂的
正如@qujck已经指出的,您不需要使用自动布线,只需进行以下注册:
container.Register<TelemetryClient>(() =>
new TelemetryClient(/*whatever values you need*/),
requestOrTransientLifestyle);
在这里,我假设TelemetryClient
是线程安全的,可以作为单例工作。否则,您可以执行以下操作:
container.RegisterSingleton<ITelemetryLogger>(
new TelemetryClientAdapter(() => new TelemetryClient(...)));
Microsoft.ApplicationInsights.TelemetryClient,它没有接口,是一个密封类,具有2个构造函数
此TelemetryClient
是一种框架类型,并且是
我发现了一篇文章,展示了如何重写容器的构造函数解析行为,但这似乎相当复杂
是的,这种复杂性是经过深思熟虑的,因为我们不想让人们创建具有多个构造函数的组件,因为这是非常复杂的
正如@qujck已经指出的,您不需要使用自动布线,只需进行以下注册:
container.Register<TelemetryClient>(() =>
new TelemetryClient(/*whatever values you need*/),
requestOrTransientLifestyle);
在这里,我假设TelemetryClient
是线程安全的,可以作为单例工作。否则,您可以执行以下操作:
container.RegisterSingleton<ITelemetryLogger>(
new TelemetryClientAdapter(() => new TelemetryClient(...)));
如果您不想走抽象/包装的道路。在测试中,您可以简单地将AppInsights端点定向到一个模拟的轻量级http服务器(在ASP.NET核心中这是很平常的) appInsightsSettings.json
"ApplicationInsights": {
"Endpoint": "http://localhost:8888/v2/track"
}
如果不想走抽象/包装路径,如何在ASP.NET核心中设置“TestServer”。在测试中,您可以简单地将AppInsights端点定向到一个模拟的轻量级http服务器(在ASP.NET核心中这是很平常的) appInsightsSettings.json
"ApplicationInsights": {
"Endpoint": "http://localhost:8888/v2/track"
}
如何在ASP.NET Core中设置“TestServer”另一个不走抽象路线的选项是在运行测试之前禁用遥测:
TelemetryConfiguration.Active.DisableTelemetry=true代码>另一个不走抽象路线的选项是在运行测试之前禁用遥测:
TelemetryConfiguration.Active.DisableTelemetry=true
Application Insights通过模拟遥测通道对遥测客户端进行单元测试
TelemetryChannel
实现了ITelemetryChannel
,因此很容易模拟和注入。在本例中,您可以记录消息,然后稍后从断言的Items
中收集消息
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);
公共类MockTelemetryChannel:ITelemetryChannel
{
公共物品
{
得到;
私人设置;
}
...
公共无效发送(ITelemetry项)
{
项目。添加(项目);
}
}
...
MockTelemetryChannel=新的MockTelemetryChannel();
遥测配置=新的遥测配置
{
遥测通道=模拟遥测通道,
InstrumentationKey=Guid.NewGuid().ToString()
};
configuration.TelemetryInitializers.Add(新操作相关TelemetryInitializer());
遥测客户端遥测客户端=新的遥测客户端(配置);
容器寄存器(遥测客户端);
Application Insights通过模拟遥测通道对遥测客户端进行单元测试
TelemetryChannel
实现了ITelemetryChannel
,因此很容易模拟和注入。在本例中,您可以记录消息,然后稍后从断言的Items
中收集消息
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);
公共类MockTelemetryChannel:ITelemetryChannel
{
公共物品
{
得到;
私人设置;
}
...
公共无效发送(ITelemetry项)
{
项目。添加(项目);
}
}
...
MockTelemetryChannel=新的MockTelemetryChannel();
遥测配置=新的遥测配置
{
遥测通道=模拟遥测通道,
InstrumentationKey=Guid.NewGuid().T
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;
}
[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);
}