Question

I am trying to write a Unit Test using JustMock that ignores an Event.

I do not want to test the Event itself, as it calls all sorts of internal functions that would require a massive amount of effort to Mock.

Here is a quick bit of example code:

public class Sample
{
    public delegate void OneParameterFunction(int someVar);
    public event OneParameterFunction TheEvent;               

    public Sample()
    {            
        TheEvent += CallMe;
    }

    public void CallMe(int someVar)
    {
        Debug.WriteLine("CallMe was fired with parameter: " + someVar);
    }

    public void FireEvent()
    {
        // do stuff, business logic here...
        if (TheEvent != null)
            TheEvent(3);
    }
}

And here is the test I would Love to write, but cannot:

[TestClass]
class EventMocking
{
    [TestMethod]
    public void DoNothingOnEvent()
    {
        var s = new Sample();

        Mock.Arrange(() => s.TheEvent(Arg.AnyInt))            
            .DoNothing();

        Mock.Arrange(() => s.CallMe(Arg.AnyInt))
            .OccursNever();

        s.FireEvent();

        Mock.Assert(() => s.CallMe(Arg.AnyInt));
    }
}

But I receive the following compiler error:

Error   1   The event 'Sample.TheEvent' can only appear on the left hand side of += or -= (except when used from within the type 'Sample')  C:\BizObjectTests\EventMocking.cs

Does anyone have any suggestions about how to stop an Event from propagating? I also do not want to Mock.Create<T> for a number of reasons, one being I would again, have to setup a lot more test data/objects.

Was it helpful?

Solution

It's impossible to mock the delegate invocation itself, since it's implemented internally by the JIT.

You have several alternative options. If you raise the event in a method dedicated for that purpose (as in your sample), then you can simply mock that method instead. So, in your sample that becomes:

Mock.Arrange(() => s.FireEvent()).DoNothing();

If that's not possible then you can mock the method that adds handlers to the event (the one called when Sample += ... is invoked). You can do this like so:

var mock = Mock.Create<Sample>(); // we need this just for the following arrangement
Mock.ArrangeSet(() => mock.TheEvent += null).IgnoreInstance().IgnoreArguments().DoNothing();

var real = new Sample(); // TheEvent += CallMe will now do nothing
real.FireEvent(); // TheEvent is empty

Finally, as a third option, you can remove all handlers from the event using reflection at some point where you know the event is just about to be fired, or that no one else will attach to it:

new PrivateAccessor(real).SetField("TheEvent", null);
real.FireEvent(); // TheEvent is null right now

Caveat: this last option is dependent on the compiler implementation. It will work for event declarations in C# code, but will not work for VB events.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top