4

I am trying to find the best approach for using MessageSender in my Azure function as the connection to ServiceBus, in order to unit test it. I have been unable to find the best way to write my code to unit test it.

Code

public static class SomeFunction
{        
    [FunctionName("SomeFunction")]
    public static async Task Run(
          [ServiceBusTrigger("someQueue", Connection = ConnectionString)]Message message
        , [ServiceBus("someQueue", Connection = ConnectionString")]MessageSender messagesQueue
        , MessageReceiver receiver
        , string lockToken)
    {
        ...some code
    }
}          

I tried to change the MessageSender to IMessageSender but got the following binding error

Binding Error

System.InvalidOperationException: "Can't bind ServiceBus to type 'Microsoft.Azure.ServiceBus.Core.IMessageSender."

Then switched it back to the MessageSender and tried mocking it and once I ran my test I got the error below.

Unit Test Example

[TestMethod]
public async Task Run_ValidMessage_Expect_Run_Succesfully()
{
    var sbBuilder = new ServiceBusConnectionStringBuilder(ActualSbConnectionString);
    Mock<MessageSender>sender = new Mock<MessageSender>(sbBuilder, RetryPolicy.Default);
    SomeFunction.Run(_sender.Object);
}
Error: 
threw exception: 
    System.ArgumentException: The argument  is null or white space.
  Stack Trace: 
    at MessageSender.ctor(String entityPath, String transferDestinationPath, Nullable`1 entityType, ServiceBusConnection serviceBusConnection, ICbsTokenProvider cbsTokenProvider, RetryPolicy retryPolicy)
    at MessageSender.ctor(String connectionString, String entityPath, RetryPolicy retryPolicy)

I have been unable to write the code with a working unit test and function. any pointers would be helpful.

Edit: included omitted args

2 Answers 2

1

From your code, there are few errors, firstly it should be ServiceBusTrigger binding not the ServiceBus cause you don't show your trigger bingding or you omit it. Then if your trigger is ServiceBusTrigger, you could only bind MessageReceiver, MessageSender is for ServiceBus. Sample code.

And the below is my test code, ServiceBusTrigger to receive the message, ServiceBus to send the message. I use the Microsoft.Azure.WebJobs.Extensions.ServiceBus 3.0.3.

[FunctionName("Function1")]
    public static async System.Threading.Tasks.Task RunAsync([ServiceBusTrigger("myqueue", Connection = "ConnectionString")]
        MessageReceiver messageReceiver,
        ILogger log, 
        [ServiceBus("test", Connection = "ConnectionString")]MessageSender messagesQueue)
    {
        Console.Write(messageReceiver.Path);

        Message m1 = await messageReceiver.PeekAsync();

        await messagesQueue.SendAsync(m1);

    }

This works for me, you could have a try if this code is what you want. If you still have problem, please feel free to let me know.

Sign up to request clarification or add additional context in comments.

1 Comment

I included the items that I omitted. I am looking to unit-test the code.
0

A bit late to the party, but I hit this issue and found a work around.

Instead of having the MessageSender as a parameter in the Run method, I ctor inject a Func<MessageSender> into the function class.

Note: I acknowledge it would probably be better if I could unit test without needing to go down this approach, but I couldn't get it working any other way...

e.g.

   public class SomeFunction
   {
      private readonly Func<IMessageSender> _messageSenderFactory;

      public SomeFunction(
         Func<IMessageSender> messageSenderFactory)
      {
         _messageSenderFactory = messageSenderFactory;
      }

      [FunctionName("SomeFunction")]
      public static async Task Run(
         [ServiceBusTrigger("someQueue", Connection = ConnectionString)]Message message,
         MessageReceiver receiver, 
         string lockToken)
      {
         // ...
         var messageSender = _messageSenderFactory.Invoke();
         await messageSender.SendAsync(message);
      }
   }

Have to register Func<IMessageSender> for DI in startup.cs:

         services.AddSingleton<Func<IMessageSender>>(() =>
            new MessageSender(Environment.GetEnvironmentVariable("SERVICE_BUS_CONNECTION"),
               Environment.GetEnvironmentVariable("someQueue")));

Can then mock the Func<IMessageSender> in unit tests.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.