42

I'm just starting with TDD and could solve most of the problems I've faced on my own. But now I'm lost: How can I check if events are fired? I was looking for something like Assert.Raise or Assert.Fire but there's nothing. Google was not very useful, most of the hits were suggestions like foo.myEvent += new EventHandler(bar); Assert.NotNull(foo.myEvent); but that proves nothing.

Thank you!

jdhurst
  • 4,365
  • 1
  • 20
  • 21
atamanroman
  • 11,607
  • 7
  • 57
  • 81

9 Answers9

58

Checking if events were fired can be done by subscribing to that event and setting a boolean value:

var wasCalled = false;
foo.NyEvent += (o,e) => wasCalled = true;

...

Assert.IsTrue(wasCalled);

Due to request - without lambdas:

var wasCalled = false;
foo.NyEvent += delegate(o,e){ wasCalled = true;}

...

Assert.IsTrue(wasCalled);
AlG
  • 14,697
  • 4
  • 41
  • 54
Dror Helper
  • 30,292
  • 15
  • 80
  • 129
  • This is incredibly old... I know... I'm curious to know... is there a possibility that the assert would be called before the code had reached the event portion? I feel as if the code is so simple that this value would be set before the assert ran. – Maderas Jun 21 '18 at 15:53
  • 1
    The assert could be called before the value is set only if the event occurs in another thread. In that case you'll need to replace the Boolean value with WaitHandle and wait until it's signaled by the event – Dror Helper Jun 27 '18 at 12:02
19

I prefer to do as follows:

var wait = new AutoResetEvent(false);
foo.MeEvent += (sender, eventArgs) => { wait.Set(); };
Assert.IsTrue(wait.WaitOne(TimeSpan.FromSeconds(5)));

Advantages: Supports multithreading scenario (if handler is invoked in different thread)

aderesh
  • 927
  • 10
  • 22
13

If you know the event will be fired synchronously:

bool eventRaised = false;
Customer customer = new Customer() { Name = "Carl" };
customer.NameChanged += (sender, e) => { eventRaised = true; };

customer.Name = "Sam";

Assert.IsTrue(eventRaised);

If the event may be fired asynchronously:

ManualResetEvent eventRaised = new ManualResetEvent(false);
Customer customer = new Customer() { Name = "Carl" };
customer.NameChanged += (sender, e) => { eventRaised.Set(); };

customer.Name = "Sam";

Assert.IsTrue(eventRaised.WaitOne(TIMEOUT));

However, some say testing asynchronous behavior should be avoided.

Clay
  • 10,885
  • 5
  • 47
  • 44
6

I recently had to do this, and below is what I came up with. The reason I did not do what the other posts said, is I do not like the idea of a variable keeping state and having to reset it "manually" between multiple events.

Below is the code of the ClassUnderTest with NameChanged event that is tested in MyTests tests:

public class ClassUnderTest {
    private string name;
    public string Name {
        get { return this.name; }
        set {
            if (value != this.name) {
                this.name = value;
                NameChanged(this, new PropertyChangedEventArgs("Name"));
            }
        }
    }

    public event EventHandler<PropertyChangedEventArgs> NameChanged = delegate { };
}

[TestFixture]
public class MyTests {
    [Test]
    public void Test_SameValue() {
        var t = new ClassUnderTest();
        var e = new EventHandlerCapture<PropertyChangedEventArgs>();
        t.NameChanged += e.Handler;

        Event.Assert(e, Event.IsNotRaised<PropertyChangedEventArgs>(), () => t.Name = null);
        t.Name = "test";
        Event.Assert(e, Event.IsNotRaised<PropertyChangedEventArgs>(), () => t.Name = "test");
    }
    [Test]
    public void Test_DifferentValue() {
        var t = new ClassUnderTest();
        var e = new EventHandlerCapture<PropertyChangedEventArgs>();
        t.NameChanged += e.Handler;

        Event.Assert(e, Event.IsPropertyChanged(t, "Name"), () => t.Name = "test");
        Event.Assert(e, Event.IsPropertyChanged(t, "Name"), () => t.Name = null);
    }
}

The supporting classes are below. The classes can be used with any EventHandler<TEventArgs> or expanded to other delegates. Event tests can be nested.

/// <summary>Class to capture events</summary>
public class EventHandlerCapture<TEventArgs> where TEventArgs : EventArgs {
    public EventHandlerCapture() {
        this.Reset();
    }

    public object Sender { get; private set; }
    public TEventArgs EventArgs { get; private set; }
    public bool WasRaised { get; private set; }

    public void Reset() {
        this.Sender = null;
        this.EventArgs = null;
        this.WasRaised = false;
    }

    public void Handler(object sender, TEventArgs e) {
        this.WasRaised = true;
        this.Sender = sender;
        this.EventArgs = e;
    }
}

/// <summary>Contains things that make tests simple</summary>
public static class Event {
    public static void Assert<TEventArgs>(EventHandlerCapture<TEventArgs> capture, Action<EventHandlerCapture<TEventArgs>> test, Action code) where TEventArgs : EventArgs {
        capture.Reset();
        code();
        test(capture);
    }
    public static Action<EventHandlerCapture<TEventArgs>> IsNotRaised<TEventArgs>() where TEventArgs : EventArgs {
        return (EventHandlerCapture<TEventArgs> test) => {
            NUnit.Framework.Assert.That(test.WasRaised, Is.False);
        };
    }
    public static Action<EventHandlerCapture<PropertyChangedEventArgs>> IsPropertyChanged(object sender, string name) {
        return (EventHandlerCapture<PropertyChangedEventArgs> test) => {
            NUnit.Framework.Assert.That(test.WasRaised, Is.True);
            NUnit.Framework.Assert.That(test.Sender, Is.SameAs(sender));
            NUnit.Framework.Assert.That(test.EventArgs.PropertyName, Is.EqualTo(name));
        };
    }
}
earlNameless
  • 2,878
  • 20
  • 25
  • Cant believe this did not get more upvotes. This is a great solution. FluentAssertions has a nice Event monitoring solution that works nicely with Nunit. It looks much like this – sacha barber Jul 11 '19 at 10:59
3

Using NUnit and Moq you can do more robust event testing.

Mock Class used to monitor event triggers:

public class AssertEvent { public virtual void Call(string obj) { } }
Mock<AssertEvent> EventMock;
AssertEvent Evt;

Setup for event Triggers:

[SetUp]
public void TestInit() {
    EventMock = new Mock<AssertEvent>();
    Evt= EventMock.Object;
}

Using Mock Objects in Tests:

[Test]
public void TestMethod() {
    myObject.Event1 += (sender, args) => Evt.Call("Event1Label");
    myObject.Event2 += (sender, args) => Evt.Call("Event2Label");
    myObject.Event3 += (sender, args) => Evt.Call("Event3Label");        

    myObject.SomeEventTrigger();

    EventMock.Verify(m => m.Call("Event1Label"), Times.Exactly(1));
    EventMock.Verify(m => m.Call("Event2Label"), Times.Never());
    EventMock.Verify(m => m.Call("Event3Label"), Times.Between(1,3);

}
Greg
  • 1,549
  • 1
  • 20
  • 34
1

I would just use FluentAssertions along with Nunit : https://fluentassertions.com/eventmonitoring/ it works really well. Here is an example from the docs

var subject = new EditCustomerViewModel();
using (var monitoredSubject = subject.Monitor())
{
    subject.Foo();
    monitoredSubject.Should().Raise("NameChangedEvent");
}
sacha barber
  • 2,214
  • 1
  • 24
  • 37
0

You can add your custom event handler which, for example, increments some integer field in test case class. And then check if field was incremented.

DixonD
  • 6,557
  • 5
  • 31
  • 52
0

Not really done this myself, but maybe you could add a dummy event handler to the event you wanna subscribe to and have it update a local boolean variable so that after the method is fired you can check the state of that boolean to see if the event was fired?

Something like:

bool eventFired = false;
foo.MyEvent += (s, e) => { eventFired = true };

Assert.IsTrue(eventFired);
theburningmonk
  • 15,701
  • 14
  • 61
  • 104
0

@theburningmonk: A ";" is missing. Corrected version is:

bool eventFired = false;
foo.MyEvent += (s, e) => { eventFired = true; };
Assert.IsTrue(eventFired);

Cheers! ;-)

Manfred
  • 5,320
  • 3
  • 35
  • 29