FakeItEasy: Registering a handler on a fake Wrapping an event does not work

Hello,

I want to be able to tell whether a method has been called on an object, but still use the actual implementation of this object.

To do this, I’m creating a fake for the interface, wrapping an instance of its implementation.

It works for methods, but not for events: trying to add an event handler on the faked event results in the concrete event never receiving the call to register the event handler.

Demo (the test fails):

[TestClass]
public class UnitTest1
{
	[TestMethod]
	public void TestMethod1()
	{
		var success = false;
		var instance = new MyClass();

		var fake = A.Fake<IInterface>(x => x.Wrapping(instance));

		fake.Event += (s, e) => success = true;

		fake.RaiseEvent();

		if (!success)
			Assert.Fail();
	}

	public interface IInterface
	{
		void RaiseEvent();
		event EventHandler Event;
	}

	public class MyClass : IInterface
	{
		public event EventHandler Event;
		public void RaiseEvent() => Event?.Invoke(this, EventArgs.Empty);
	}
}

I did not find any documentation about this behavior. Is this expected? Is there any way around that?

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 15 (12 by maintainers)

Most upvoted comments

This change has also been released as part of FakeItEasy 7.0.0.

This change has been released as part of FakeItEasy 7.0.0-beta.1.

  • Fear of having the event-raising functionality be disabled (with no way to re-enable it) by fake object-level configuration such as A.CallTo(fake).…

That’s a good point. Any call to A.CallTo(fake) that doesn’t filter out event accessors would disable the default behavior, so it would be a breaking change. That being said, I think it would be the correct behavior.

  • The first point may apply to strict fakes as well. But I’m not sure it would’ve at the time

Indeed. Currently, .Strict() has no effect on the behavior of events. Moving EventRule after the user rules would change that (again, a breaking change, but again, I think it would be the correct behavior).

I don’t think the change would affect that many people, but it’s enough of a breaking change that it would have to go in a major release.

@mlaily, I’m not sure how ugly your workaround is, but since the event listener registration is applied to the Fake, and the Raise call passes through to the wrapped object, if you haven’t struck upon this solution, one workaround is to have the Fake handle the Raise:

var success = false;
var instance = new MyClass();
var fake = A.Fake<IInterface>(x => x.Wrapping(instance));

fake.Event += (s, e) => success = true;

// THIS IS NEW!
A.CallTo(() => fake.RaiseEvent()).Invokes(() => fake.Event += Raise.WithEmpty());

fake.RaiseEvent();

if (!success)
    Assert.Fail();