Ran*_*age 16 c# unit-testing azure simple-injector azure-application-insights
我有一个MVC网络应用程序,我正在使用Simple Injector进行DI.我的几乎所有代码都包含在单元测试中.但是,现在我已经在某些控制器中添加了一些遥测调用,我在设置依赖项时遇到了麻烦.
遥测调用用于将指标发送到Microsoft Azure托管的Application Insights服务.该应用程序未在Azure中运行,只是一个带有ISS的服务器.AI门户网站会告诉您有关应用程序的各种信息,包括使用遥测库发送的任何自定义事件.因此,控制器需要一个Microsoft.ApplicationInsights.TelemetryClient实例,该实例没有接口,并且是一个带有2个构造函数的密封类.我试着像这样注册它(混合生活方式与这个问题无关,我只是为了完整而包含它):
// hybrid lifestyle that gives precedence to web api request scope
var requestOrTransientLifestyle = Lifestyle.CreateHybrid(
() => HttpContext.Current != null,
new WebRequestLifestyle(),
Lifestyle.Transient);
container.Register<TelemetryClient>(requestOrTransientLifestyle);
Run Code Online (Sandbox Code Playgroud)
问题是,由于TelemetryClient有2个构造函数,SI抱怨并且验证失败.我找到了一篇文章,展示了如何覆盖容器的构造函数解析行为,但这似乎相当复杂.首先,我想要备份并提出这个问题:
如果我没有使TelemetryClient成为一个注入的依赖项(只是在类中创建一个新的),那么在每次运行单元测试时是否会将遥测发送到Azure,从而产生大量错误数据?或者Application Insights是否足够聪明,知道它在单元测试中运行,而不是发送数据?
对此问题的任何"见解"将不胜感激!
谢谢
Ste*_*ven 21
Microsoft.ApplicationInsights.TelemetryClient,没有接口,是一个密封类,有2个构造函数.
这TelemetryClient是一种框架类型,框架类型不应由容器自动连接.
我找到了一篇文章,展示了如何覆盖容器的构造函数解析行为,但这似乎相当复杂.
是的,这种复杂性是刻意的,因为我们希望阻止人们创建具有多个构造函数的组件,因为这是一种反模式.
正如@qujck已经指出的那样,您可以简单地进行以下注册,而不是使用自动布线:
container.Register<TelemetryClient>(() =>
new TelemetryClient(/*whatever values you need*/),
requestOrTransientLifestyle);
Run Code Online (Sandbox Code Playgroud)
或者Application Insights是否足够聪明,知道它在单元测试中运行,而不是发送数据?
非常不可能.如果要测试依赖于此的类,TelemetryClient最好使用虚假实现,以防止单元测试变得脆弱,缓慢或污染Insight数据.但即使测试不是问题,根据依赖性倒置原则,您应该依赖于(1)由您自己的应用程序定义的(2)抽象.使用时,你失败了两个点TelemetryClient.
你应该做的是定义一个(或者甚至多个)抽象TelemetryClient,特别是为你的应用量身定制.因此,不要试图TelemetryClient用它可能的100种方法来模仿API,而只是在控制器实际使用的接口上定义方法,并使它们尽可能简单,这样你就可以使控制器的代码更简单 - 而且 - 你的单元测试更简单.
在定义了良好的抽象之后,您可以创建一个TelemetryClient内部使用的适配器实现.我想象你注册这个适配器如下:
container.RegisterSingleton<ITelemetryLogger>(
new TelemetryClientAdapter(new TelemetryClient(...)));
Run Code Online (Sandbox Code Playgroud)
在这里,我假设它TelemetryClient是线程安全的,可以作为单例工作.否则,您可以执行以下操作:
container.RegisterSingleton<ITelemetryLogger>(
new TelemetryClientAdapter(() => new TelemetryClient(...)));
Run Code Online (Sandbox Code Playgroud)
这里的适配器仍然是一个单例,但提供了一个允许创建的委托TelemetryClient.另一种选择是让适配器在TelemetryClient内部创建(并可能配置).这可能会使注册更简单:
container.RegisterSingleton<ITelemetryLogger>(new TelemetryClientAdapter());
Run Code Online (Sandbox Code Playgroud)
Jam*_*ood 14
Application Insights有一个通过模拟进行单元测试的示例.TelemetryClientTelemetryChannel
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);
Run Code Online (Sandbox Code Playgroud)
我有很多成功的使用乔希Rostad的文章写我的模拟TelemetryChannel并注入到我的测试.这是模拟对象:
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()
{
}
}
Run Code Online (Sandbox Code Playgroud)
然后在我的测试中,一个本地方法来启动模拟:
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;
}
Run Code Online (Sandbox Code Playgroud)
最后,运行测试!
[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);
}
Run Code Online (Sandbox Code Playgroud)
如果您不想走抽象/包装路径。在测试中,您可以简单地将 AppInsights 端点定向到模拟轻量级 http 服务器(这在 ASP.NET Core 中很简单)。
appInsightsSettings.json
"ApplicationInsights": {
"Endpoint": "http://localhost:8888/v2/track"
}
Run Code Online (Sandbox Code Playgroud)
如何在 ASP.NET Core 中设置“TestServer” http://josephwoodward.co.uk/2016/07/integration-testing-asp-net-core-middleware
| 归档时间: |
|
| 查看次数: |
6455 次 |
| 最近记录: |