2

I can't seem to figure out how to mock this method properly. The method I want to mock is below:

public virtual void Publish<TEvent>(TEvent evt) where TEvent : IDomainEvent {
    HandlerInvoker.Invoke(evt);
}

An example of a TEvent that I want to mock is:

public interface IOrderPlaced : IDomainEvent {}

I am able to mock this if I use:

mock.Setup(h => h.Publish(It.IsAny<IOrderPlaced>));

However, I wish to mock the method for all interfaces that derive from IDomainEvent like so:

mock.Setup(h => h.Publish(It.IsAny<IDomainEvent>));

but that doesn't work at all. It only works when I setup the mock using the specific interface. But that is completely unrealistic in my application as I have over 100 interfaces that derive from IDomainEvent. Not to mention it would be a beast to maintain if I had to mock each one individually. Does anyone see anything I'm doing wrong?

Brian
  • 1,027
  • 2
  • 11
  • 17

1 Answers1

1

The problem you're experiencing is that every different derivation of IDomainEvent that you call Publish<T>() with is implemented as a method with a different signature.

Moq only mocks a single signature when you use Setup()

In fact, mock.Setup(h => h.Publish(It.IsAny<IDomainEvent>)); will only mock Publish<IDomainEvent>(x) when x is specifically treated by the compiler as type IDomainEvent. e.g.

OrderPlaced event = new OrderPlaced();
mock.Object.Publish((IDomainEvent)event);

Unfortunately there just isn't a way to Setup every signature of a mock generic method. You can't even iterate through all IDomainEvent-derived types at runtime with reflection - because you'd hit exactly the same problem with attempting to access the generic It.IsAny() method.

For more info, read this related answer to a different question.

...

If your mock behaviour is Loose, then the methods will succeed regardless. So I assume you want to attach additional behaviour, such as .Returns() or .Callback(). Perhaps if you go into more detail about the desired outcome we might be able to find an alternate solution?

Community
  • 1
  • 1
perfectionist
  • 4,256
  • 1
  • 23
  • 34
  • My understanding of generics is not perfect. Please help me improve my answer if I'm off the mark. – perfectionist Mar 05 '12 at 22:47
  • Yes, I do want to use .Callback() so I can add the IDomainEvent to a list for tests that check if the specific IDomainEvent was Published(). – Brian Mar 06 '12 at 14:32
  • Ok, we can probably solve your specific problem manually, either by creating a completely manual mocking class, or by wrapping a Moq Mock<>... Is collating and checking a list of IDomainEvents Published **all** you want to do? (I'll come back to this in a few hours, after work) – perfectionist Mar 06 '12 at 15:23
  • Well, it's not all I want to do. The Publish() is called inside of other methods that are being tested as well. So the method may calculate some value and then Publish(), and I would want to test both. – Brian Mar 06 '12 at 16:33
  • not quite clear on what you just said. Can you be specific on what else **the mock** needs to be set up to do other than collate the calls to publish()? – perfectionist Mar 06 '12 at 17:25
  • The only thing that needs to be mocked is the calls to Publish(). However, other calls on the same object need to pass through to the appropriate methods. For instance, if the object has methods: Publish(), Process(), and Void(), Publish() should be mocked while the other two are executed as they normally would in the application. Does that make sense? I may have just figured out a way to do it using a fake object and dependency injection. I'm working on it now. – Brian Mar 06 '12 at 19:03
  • I think you're on the right lines. Unless there's a fancy mocking library out there that can handle generic methods, a custom injected mock might be the way. Good luck! – perfectionist Mar 06 '12 at 19:43