【问题标题】:Unit Testing ServiceBus.Message. How to set value for SystemProperties.LockToken?单元测试 ServiceBus.Message。如何设置 SystemProperties.LockToken 的值?
【发布时间】:2019-10-10 01:23:25
【问题描述】:

我想测试一个使用queueClient.RegisterMessageHandler(MyCallBack, messageHandlerOptions) 向 QueueClient 注册的消息处理程序回调。

public Task MyCallBack(Message msg, CancellationToken token)
{
   // Read msg. Do something

   // Since everything ran fine, complete the msg.
   await _client.CompleteAsync(msg.SystemProperties.LockToken);
}

现在,作为单元测试的一部分,我调用MyCallBack。因为我传递了一个 有效 消息。我希望client.CompleteAsync() 被调用。但是测试会抛出异常。

System.InvalidOperationException: Operation is not valid due to the current state of the object.

这是因为msg.SystemProperties.LockToken没有设置(这是因为消息实际上不是由具有ReceiveMode.PeekLock模式的客户端从队列中读取的)。

有没有办法设置/模拟这个,以便我可以使用虚拟字符串作为令牌运行我的测试?


PS:我知道我可以在实际访问 LockToken 字段之前检查msg.SystemProperties.IsLockTokenSet;但即使在那种情况下,如果_client.CompleteAsync() 被调用,我也永远无法进行单元测试。

【问题讨论】:

  • 我尝试在Message 周围放置一个包装器,但这需要在SystemPropertiesCollection 周围放置一个包装器。看起来很难模仿。也许您可以将“做某事”提取到另一个依赖项中,并将消息或其正文(至少您可以通过传递)传递给它。这样你就可以测试那个类,而这个方法实际上并没有做任何值得测试的事情。

标签: c# azureservicebus azure-servicebus-queues


【解决方案1】:

这里是如何创建一个用于测试的消息并设置 LockToken:

var message = new Message();
var systemProperties = message.SystemProperties;
var type = systemProperties.GetType();
var lockToken = Guid.NewGuid();
type.GetMethod("set_LockTokenGuid", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(systemProperties, new object[] { lockToken });
type.GetMethod("set_SequenceNumber", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(systemProperties, new object[] { 0 });

我希望我能够想出一些不涉及反思的东西。

【讨论】:

  • 不错的 hack,我所做的一个小改动是在 Invoke 之前添加 ? 运算符以避免可能的 NRE
  • 非常感谢您非常!对于任何测试 DeliveryCount 的人,它将是 type.GetMethod("set_DeliveryCount",...
【解决方案2】:

我创建了一个包装方法GetLockToken(),如果它在消息上设置,则返回LockToken 字符串,否则返回null(而不是抛出异常)。

private string GetLockToken(Message msg)
{
    // msg.SystemProperties.LockToken Get property throws exception if not set. Return null instead.
    return msg.SystemProperties.IsLockTokenSet ? msg.SystemProperties.LockToken : null;
}

原来对CompleteAsync()的方法调用现在修改为:

await _client.CompleteAsync(GetLockToken(message));

注意:上述更改不会改变预期的行为!在生产场景中,调用CompleteAsync(null) 仍会引发异常:)(根据需要)。

通过上述更改,我现在可以这样设置我的模拟:

var mock= new Mock<IQueueClient>();
mock.Setup(c => c.CompleteAsync(/*lockToken*/ null))
               .Returns(Task.CompletedTask);

【讨论】:

    猜你喜欢
    • 2016-08-19
    • 1970-01-01
    • 2014-11-25
    • 1970-01-01
    • 1970-01-01
    • 2018-04-16
    • 2015-01-25
    • 2014-03-02
    • 1970-01-01
    相关资源
    最近更新 更多