gpt4 book ai didi

c# - 在 C# 中,是否可以模拟出 IMessageReceiver 和相关类进行单元测试?

转载 作者:行者123 更新时间:2023-12-04 02:33:11 29 4
gpt4 key购买 nike

我希望为以下类创建单元测试:

    public class ServiceBusClient {
private readonly IMessageReceiver messageReceiver;
private readonly int maximumMessages;

public ServiceBusClient(IMessageReceiver messageReceiver, int maximumMessages) {
this.messageReceiver = messageReceiver;
this.maximumMessages = maximumMessages;
}

public async Task<List<EnergyUser>> ReceiveEnergyUsersAsync() {
List<EnergyUser> energyUsers = new List<EnergyUser>();
List<string> lockTokens = new List<string>();

this.ReceiveMessages()
.ForEach((message) => {
if (message.Body != null) {
energyUsers.Add(JsonConvert.DeserializeObject<EnergyUser>(Encoding.UTF8.GetString(message.Body)));
}
lockTokens.Add(message.SystemProperties.LockToken);
});

_ = this.messageReceiver.CompleteAsync(lockTokens);
return await Task.FromResult(energyUsers);
}

private List<Message> ReceiveMessages() {
return this.messageReceiver.ReceiveAsync(this.maximumMessages)
.GetAwaiter()
.GetResult()
.ToList();
}
}

可以看出它依赖于Microsoft.Azure.ServiceBus.Core.IMessageReceiver。 .

我第一次尝试模拟这个是使用 Moq .如果我创建一个 new Mock<IMessageReceiver>(),我会期望, 我应该可以将它注入(inject) public ServiceBusClient(IMessageReceiver messageReceiver, int maximumMessages) ,而是编译器告诉我“错误 CS1503 参数 1:无法从‘Moq.Mock ’转换为‘Microsoft.Azure.ServiceBus.Core.IMessageReceiver’”...

然后我想我会尝试手动模拟类:

    internal class MockMessageReceiver : IMessageReceiver {
public int ReceivedMaxMessgeCount { get; set; }
public IList<Message> ReturnMessages { get; set; }
Task<IList<Message>> IMessageReceiver.ReceiveAsync(int maxMessageCount) {
this.ReceivedMaxMessgeCount = maxMessageCount;
return Task.FromResult(this.ReturnMessages);
}

public IEnumerable<string> ReceivedLockTokens { get; set; }
Task IMessageReceiver.CompleteAsync(IEnumerable<string> lockTokens) {
this.ReceivedLockTokens = lockTokens;
return Task.Delay(1);
}

// Many functions which do nothing just to satisfy the bloated interface.
}

这将使我能够成功提供消息,除了我提供的消息不包含 SystemProperties,因此 ServiceBusClient 将在 lockTokens.Add(message.SystemProperties.LockToken) 处抛出错误.

事实证明,Microsoft.Azure.ServiceBus.Message实现不为 public SystemPropertiesCollection SystemProperties 提供 setter , 所以要设置它(除非有人有更好的方法),我需要创建我自己的 Message 实现:

    public class MockMessage : Message {
public MockMessage(byte[] body) => base.Body = body;

public new SystemPropertiesCollection SystemProperties {
get { return this.SystemProperties; }
set { this.SystemProperties = value; }
}
}

现在,我可以初始化 SystemPropertiesCollection,但问题是 SystemPropertiesCollection 中没有任何属性实际上包含设置,所以我的测试仍然会失败。

然后我想:让我们为“SystemPropertiesCollection”创建一个模拟(不要介意我们开始在“太多模拟”的危险水域中游泳......但是当我尝试扩展这个类时,我的编译器提示因为 SystemPropertiesCollection 实际上是一个密封类,所以我无法扩展它。

那么,现在我回到原点。

关于如何为 ServiceBusClient 创建良好的单元测试有什么想法吗?

最佳答案

长话短说

遇到了同样的问题,并通过利用反射获得对 Message.SystemPropertiesCollection 类私有(private)成员的访问权限解决了这个问题。

更详细的解释

为了避免 message.SystemProperties.LockToken 抛出 InvalidOperationException,您必须将 Message.SystemPropertiesCollection.SequenceNumber 属性设置为某个值除了-1。但是该属性的 setter 是私有(private)的,类 Message.SystemPropertiesCollection 是密封的,并且没有其他间接方法可以设置序列号而不实际通过服务总线发送消息。您不想在单元测试中执行的操作。

但通过反射调用 Message.SystemPropertiesCollection.SequenceNumber 的私有(private) setter 解决了这个障碍,让我伪造了不抛出的 Message 对象。

示例代码

此辅助方法生成不会抛出的 Message 对象。

private static Message GenerateSaneMessage()
{
var message = new Message();
var sysCollectionType = typeof(Message.SystemPropertiesCollection);
sysCollectionType.GetProperty(nameof(Message.SystemPropertiesCollection.SequenceNumber))!
.SetValue(message.SystemProperties, 0);
return message;
}

关于c# - 在 C# 中,是否可以模拟出 IMessageReceiver 和相关类进行单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62991638/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com