单元测试 ServiceBus.Message。如何设置 SystemProperties.LockToken 的值?

Unit Testing ServiceBus.Message. How to set value for SystemProperties.LockToken?

我想测试使用 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。因为我传递了 valid 消息。我希望 client.CompleteAsync() 被调用。但是测试抛出异常。

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

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

有没有办法 set/mock 这样我就可以 运行 我的测试使用虚拟字符串作为标记?


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

我创建了一个包装器方法 GetLockToken() 如果它在消息上设置 returns LockToken 字符串,否则它 returns 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);

以下是创建用于测试的消息和设置 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 });

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