I have the interfaces
public interface IBar
{
}
and
public interface IFoo
{
event EventHandler<IBar> MyEvent;
}
and a class
public class Foobar
{
public Foobar(IFoo foo)
{
foo.MyEvent += MyEventMethod;
}
private void MyEventMethod(object sender, IBar bar)
{
// do nothing
}
}
Now I want to unit test this brilliant piece of code using Moq 4:
[Test]
public void MyTest()
{
Mock<IFoo> foo = new Mock<IFoo>();
Mock<IBar> bar = new Mock<IBar>();
Foobar foobar = new Foobar(foo.Object);
foo.Raise(e => e.MyEvent += null, bar.Object);
}
From my understanding Foobar.MyEventMethod should be called through the raise. What happens is that I get a runtime exception that says System.Reflection.TargetParameterCountEception {"Parameter count mismatch."}.
Funny thing: when I Raise the following in the unit test:
foo.Raise(e => e.MyEvent += null, EventArgs.Empty, bar.Object);
Everything works as I want it. Can anybody explain why three arguments are needed for the call?
Thank you
Mocks either substitute the event source by using . Raise(), or they substitute an object that will consume another class under test's event (to assert the event was raised), in which case you use . Callback() to record "handling" the event in a local flag variable.
The EventHandler delegate is a predefined delegate that specifically represents an event handler method for an event that does not generate data. If your event does generate data, you must use the generic EventHandler<TEventArgs> delegate class.
I assume you use .NET 4.5 then. Type constraint was removed from EventHandler<TEventArgs>
which allows you to do something like this:
event EventHandler<IBar> MyEvent;
Where IBar
is just some interface.
IN 4.0, with constraint restricting TEventArgs
to be assignable to EventArgs
type, your code wouldn't compile.
As a result of this (IBar
not deriving from EventArgs
), Moq doesn't consider your event as "corresponding to Event Handler pattern", and treats it as any other delegate:
// Raising a custom event which does not adhere to the EventHandler pattern
...
// Raise passing the custom arguments expected by the event delegate
mock.Raise(foo => foo.MyEvent += null, 25, true);
Which means you have to provide all parameters, including sender.
The reason the first is not working because EventHandlers have 2 parameters (object sender, EventArgs args).
When you are setting up mocking
foo.Raise(e => e.MyEvent += null, EventArgs.Empty, bar.Object);
thee => e.MyEvent += null
is an expression to tell Moq
which event to raise,
The following 2 parameters are the 2 arguments you want to raise it with.
EventArgs.Empty, bar.Object
Note: If memory serves me right, those should be the other way around.
When you try to raise an event with 1 argument (bar.Object
) Moq
throws an exception saying that event handler requires 2 as it uses reflection to invoke it.
Your first case could be written like this:
public class Foo : IFoo
{
public event EventHandler<IBar> MyEvent;
public void OnMyEvent(IBar bar)
{
MyEvent(EventArgs.Empty)
}
}
Which gives you a compiler error: Delegate 'EventHandler' does not take 1 arguments
So that's why you need 2 parameters, as you would invoke it with the following:
public class Foo : IFoo
{
public event EventHandler<IBar> MyEvent;
public void OnMyEvent(IBar bar)
{
MyEvent(this, bar);
}
}
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