9

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

anhoppe
  • 4,287
  • 3
  • 46
  • 58
  • Can you post a sample that compiles and includes `IBar` class? After fixing obvious compilation errors your sample works under .NET 4.0 & Moq 4.2. – k.m Mar 19 '15 at 14:58
  • Sry, just hacked it in. What a surprise it did not compile ;-) Now it should – anhoppe Mar 19 '15 at 15:08

2 Answers2

8

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.

Community
  • 1
  • 1
k.m
  • 30,794
  • 10
  • 62
  • 86
  • Thanks, great answer. Really sheds some light. But I'm still missing why there are three parameters, I'd expect I'd need object sender, TEventArgs args. – anhoppe Mar 19 '15 at 15:30
  • 1
    @anhoppe: The first parameter is there to tell Moq *which* event to raise, `foo => foo.MyEvent += null` -- Moq will dissect this `Expression` and figure out you want `MyEvent` to be raised. The remaining ones are whatever your custom delegate requires (`object sender` and `EventArgs args`). – k.m Mar 19 '15 at 15:33
6

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);
    }
}
Michal Ciechan
  • 13,492
  • 11
  • 76
  • 118
  • 1
    Sender parameter is not required when event is done correctly. See: https://github.com/Moq/moq4/wiki/Quickstart#events – k.m Mar 19 '15 at 15:26
  • Thats if he uses EventHandler, not EventHandler. Tbh i didnt know that it would even work if IFoo was inheriting from EventHandler, guess i learnt something new. – Michal Ciechan Mar 19 '15 at 15:33
  • EventArgs.Empty was the missing part for me. Thank you. – Scott Ferguson May 07 '19 at 21:05