我有下一个方法,请注意我正在做
new ServiceBusClient(connectionString)
,我希望我可以模拟它,以便它抛出一个期望的异常。我正在使用 NSubstitute,但我不知道如何做到这一点。
public void Connect()
{
try
{
client = new ServiceBusClient(connectionString);
}
catch (Exception exception)
{
switch (exception)
{
case FormatException _:
logger.LogError(new ConnectionStringFormatError(connectionString));
break;
case ServiceBusException _:
logger.LogError(new ConnectionError(exception.Message));
break;
}
}
}
ServiceBusClient 的构造函数有参数,所以我无法模拟类本身。有什么方法可以得到这个吗?
为了使此代码可测试并模拟
ServiceBusClient
,您不应直接在代码中使用它,而应通过抽象来使用它。
因此,首先创建工厂的抽象,它将为您创建服务总线客户端。像这样的东西:
public interface IServiceBusClientFactory
{
ServiceBusClient GetServiceBusClient();
}
然后你需要实现这个抽象。这个抽象的实现将创建
ServiceBusClient
的实例
public class ServiceBusClientFactory : IServiceBusClientFactory
{
private readonly string _connStr;
public ServiceBusClientFactory(string connStr)
{
if(string.IsNullOrEmpty(connStr))
{
throw new ArgumentNullException(nameof(connStr));
}
_connStr = connStr;
}
public ServiceBusClient GetServiceBusClient()
{
return new ServiceBusClient(_connStr);
}
}
然后你的客户端代码将使用
IServiceBusClientFactory
接口,你可以在单元测试中随意模拟它。
var clientMock = Substitute.For<IServiceBusClientFactory>();
clientMock.GetServiceBusClient(Arg.Any<string>()).Returns(x => throw new FormatExcepction());
当然,这需要使用 IoC - 那么您将受益于抽象的使用。
ServiceBusClient 文档 建议在应用程序的生命周期内缓存和重用客户端。因此,您的
Connect
方法不应创建新的 ServiceBusClient
客户端。相反,它应该使用在 DI 容器中创建并注入到您的类中的现有实例。在那里,您可以调用 CreateSender
或 CreateReceiver
方法。
Azure SDK GitHub 页面有一些模拟客户端的示例。比如如何mock
CreateSender
方法:
Mock<ServiceBusClient> mockClient = new();
Mock<ServiceBusSender> mockSender = new();
// This sets up the mock ServiceBusClient to return the mock of the ServiceBusSender.
mockClient
.Setup(client =>client.CreateSender(It.IsAny<string>()))
.Returns(mockSender.Object);