简体   繁体   中英

Raise an EventHandler<TEventArgs> event with a Moq instance

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

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);

the e => 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);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM