Search code examples
c#genericsgeneric-programming

How can I add generic Funcs to a list and execute them?


I am trying to create an abstraction layer for my queue to enable better integration tests. The real queue is RabbitMq through EasyNetQ.

In my program, I subscribe to events by which I link methods to certain event types -- when the event is published, I want the method executed. That obvoiusly works well when using the real thing.

It does however make my tests depend on a RabbitMq server and it does make code execute async which for one thing makes it hard to determine when the test should be considered finished.

I'd rather not use this question to discuss the decision to abstract the RabbitMq away in my tests.

To create a Stub to use in my tests, I need to store the reference to the event handlers -- and I need to execute the event handlers when events of relevant types are afterwards published.

So I think I need code like what I have written below. I can store the event handlers as Delegates but how do I invoke them? RabbitMq event handlers are on the form Func where T is the type of event the handler handles.

Func<int, Task> handle1 = ...;
Func<string, Task> handle2 = ...;

List<> l = new List<>();
l.Add( handle1);
l.Add( handle2);

foreach(Func f in l)
{
  if (f-parameter is string)
  {
    f("");
  }
  if (f-parameter is int)
  {
    f(1);
  }
}

Solution

  • I ended up with the following solution. It allows me to Fake out my use of RabbitMq entirely and have the event handlers actived for each published message, as desired. Also, it's void of specific names of event classes, allowing me to reuse it for all my integration tests of RabbitMq solutions.

    You could say that the missing piece was

    var dynamicInvoke = d1.DynamicInvoke(o)
    

    I have a 'real' front end too, relaying the calls to RabbitMq / EasyNetQ for the actual running application.

    Thanks for chipping in, @ensisNoctis!

      public class TestBusFront : IBusFront
      {
        private readonly Dictionary<string, List<Delegate>> _list;
    
        public TestBusFront()
        {
            _list=new Dictionary<string, List<Delegate>>();
        }
        public void Publish(object o)
        {
            var d = GetDelegateListForEvent(o.GetType().FullName);
            foreach (var d1 in d)
            {
                var dynamicInvoke = d1.DynamicInvoke(o);
                var task = (Task) dynamicInvoke;
                task.Wait();
            }
        }
    
        public void SubscribeAsync<T>(string s, Func<T, Task> handle)
        {
            var delegates = GetDelegateListForEvent(typeof(T).FullName);
            delegates.Add(handle);
        }
    
        private List<Delegate> GetDelegateListForEvent(string eventName)
        {
            if(!_list.ContainsKey(eventName))
                _list.Add(eventName,new List<Delegate>());
    
            return _list[eventName];
        }
    
        public void Dispose()
        {
    
        }
    }