Mock and callback a generic method with unavailable type using Moq

This is as much a note to self as a blog post. I needed to test a method that was dependent on a API with a callback action where T was scoped internal in the library that I was testing. Rather than making the type public I went ahead and tried to mock it anyway 😀

The code that we want to test is called from third party API like

await hubProxyFactory
            .Create(hubUrl, configureConnection, async p =>
                {
                    proxy = p;
                    await SendQueuedSubscriptions();
                    p.On<Message>("onEvent", OnEvent);
                },
                Reconnected, FaultedConnection, ConnectionComplete);

Message is a private type and OnEvent is of type Action<Message>. Its the OnEvent method we want to test. So we need to mock On<T> without knowing T and we need to save a reference to the OnEvent even though T is unknown. It looks like this

                    mock.Setup(x => x.On("onEvent", It.IsAny<Action<It.IsAnyType>>())).Callback(
                        (string e, MulticastDelegate callback) =>
                        {
                            onEvent = callback;
                        });

So I mock any calls to On<It.IsAny>, It.IsAny is Moq magic and it means that T can be of any type, were the first argument equals “onEvent” and the second argument is a Action where T can be of any type. I also need to hookup a callback so I can save a reference to the OnEvent delegate. One could think second argument would be Action<It.IsAnyType>. Only thing that worked was to give it the close to the metal MulticastDelegate.

To create the private type that you send into the callback you need to use reflection

        protected DotNetClientTest()
        {
            messageType = typeof(EventProxy).Assembly.GetType("SignalR.EventAggregatorProxy.Client.DotNetCore.EventAggregation.EventProxy+Message");
            typeProp = messageType.GetProperty("Type");
            genericProp = messageType.GetProperty("GenericArguments");
            eventProp = messageType.GetProperty("Event");
            idProp = messageType.GetProperty("Id");
        }

        protected void PublishEvent<T>(T @event, object constraint = null)
        {
            var type = typeof(T);
            var message = Activator.CreateInstance(messageType);
            
            typeProp.SetValue(message, type.GetFullNameWihoutGenerics());
            genericProp.SetValue(message, type.GetGenericArguments().Select(t => t.FullName).ToArray());
            eventProp.SetValue(message, JsonDocument.Parse(JsonSerializer.SerializeToUtf8Bytes(@event)).RootElement);

            onEvent.Method.Invoke(onEvent.Target, new[] { message });
        }
I instance the private type with reflection, set it members and then invoke the delegate.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s