I want to test a message handler callback that I have registered with a QueueClient using queueClient.RegisterMessageHandler(MyCallBack, messageHandlerOptions)
.
public Task MyCallBack(Message msg, CancellationToken token)
{
// Read msg. Do something
// Since everything ran fine, complete the msg.
await _client.CompleteAsync(msg.SystemProperties.LockToken);
}
Now as part of my unit test I call MyCallBack
. Since I pass a valid message. I expect client.CompleteAsync()
to be called. However the test throws an exception.
System.InvalidOperationException: Operation is not valid due to the current state of the object.
This is because msg.SystemProperties.LockToken
is not set (which is because message was not actually read from a queue by a client with ReceiveMode.PeekLock
mode).
Is there a way to set/mock this so that I can run my tests with a dummy string as a token?
PS: I know I can check for msg.SystemProperties.IsLockTokenSet
before actually accessing the LockToken field; but even in that case I will never be able to unit test if _client.CompleteAsync()
was called.
Here is how to create a Message for testing and set the 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 });
I wish that I had been able to come up with something that did not involve reflection.
I created a wrapper method GetLockToken()
which returns the LockToken
string if it set on the message, else it returns null (instead of throwing an exception).
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;
}
The original method call to CompleteAsync()
is now modified to:
await _client.CompleteAsync(GetLockToken(message));
Note: The above change doesn't change the expected behavior! In a production scenario, call to CompleteAsync(null)
would still throw an exception :) (as desired).
With above changes now I can set up my mocks as such:
var mock= new Mock<IQueueClient>();
mock.Setup(c => c.CompleteAsync(/*lockToken*/ null))
.Returns(Task.CompletedTask);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With