【发布时间】:2020-11-25 17:00:16
【问题描述】:
我正在实现我的第一个 signalR 应用程序,但在测试方面遇到了绊脚石。我正在使用Moq 框架。
不幸的是,我发现微软的HubConnection 类使用Moq 框架不容易模拟,即它没有虚拟方法,也没有直接实现接口。微软官方的suggestion是给开发者实现一个包装类和接口的,我已经做过了,见下面类HubProxy的小样例。我现在遇到的问题是如何测试被包装的HubConnection类是由包装器触发的?
我最初从 HubConnection 派生了一个类,MockedHubConnection 下面使用 new 来尝试覆盖。 MockedHubConnection 方法抛出 NotImplementedException。在下面的测试中,它断言 NotImplementedException 被抛出以表示包装的 HubConnection 已被触发。但是,我收到来自测试的 NullReference 异常:
Actual: typeof(System.NullReferenceException): Object reference not set to an instance of an object.
---- System.NullReferenceException : Object reference not set to an instance of an object.
Stack Trace:
at Microsoft.Extensions.Logging.Logger`1.Microsoft.Extensions.Logging.ILogger.BeginScope[TState](TState state)
at Microsoft.AspNetCore.SignalR.Client.HubConnection.StopAsync(CancellationToken cancellationToken)
----- Inner Stack Trace -----
at Microsoft.Extensions.Logging.Logger`1.Microsoft.Extensions.Logging.ILogger.BeginScope[TState](TState state)
at Microsoft.AspNetCore.SignalR.Client.HubConnection.StopAsync(CancellationToken cancellationToken)
我认为发生了什么,HubProxy 类的构造函数隐式地将模拟 HubConnection 转换为真实的 HubConnection 实例??
如何测试一个包装器类,该包装器类包装了一个不提供可模拟接口或可被覆盖的虚拟方法的第三方类?
Hub 代理示例 - 包装 HubConnection
public class HubProxy : IHubProxy
{
private readonly HubConnection connection;
public HubProxy(HubConnection connection)
{
this.connection = connection;
}
public Task StopAsync(CancellationToken cancel=default)
{
this.connection.StopAsync(cancel);
}
}
模拟 HubConnection - 抛出 NotImplementedException 以测试由包装器调用
public class MockHubConnection : HubConnection
{
public MockHubConnection(
IConnectionFactory connection,
IHubProtocol protocol,
EndPoint endPoint,
IServiceProvider provider,
ILoggerFactory factory,
IRetryPolicy policy
) : base(connection, protocol, endPoint, provider, factory, policy) { }
public new Task StopAsync(CancellationToken token = default)
{
throw new NotImplementedException();
}
}
模拟 HubConnection - 初始尝试 - 使用 new 覆盖
public class MockHubConnection : HubConnection
{
public MockHubConnection(
IConnectionFactory connection,
IHubProtocol protocol,
EndPoint endPoint,
IServiceProvider provider,
ILoggerFactory factory,
IRetryPolicy policy
) : base(connection, protocol, endPoint, provider, factory, policy) { }
public new Task StopAsync(CancellationToken token = default)
{
throw new NotImplementedException();
}
}
测试样本
[Fact]
public async Task HubProxy_StopAsync_Invokes_HubConnection_StopAsync()
{
var mockCon = Mock.Of<IConnectionFactory>();
var mockHubProtocol = Mock.Of<IHubProtocol>();
var mockServiceProvider = Mock.Of<IServiceProvider>();
var mockLoggerFactory = Mock.Of<ILoggerFactory>();
var mockRetryPolicy = Mock.Of<IRetryPolicy>();
var mockHubConnection = new MockHubConnection(
mockCon,
mockHubProtocol,
new IPEndPoint(0, 0),
mockServiceProvider,
mockLoggerFactory,
mockRetryPolicy
);
var _client = new HubProxy(mockHubConnection);
await Assert.ThrowsAsync<NotImplementedException>(() => _client.StopAsync());
}
【问题讨论】:
标签: c# asp.net-core moq asp.net-core-signalr