Unit testing that an event is raised in C#

I have some code that raises PropertyChanged events and I would like to be able to unit test that the events are being raised correctly.

The code that is raising the events is like

public class MyClass : INotifyPropertyChanged
{
   public event PropertyChangedEventHandler PropertyChanged;  

   protected void NotifyPropertyChanged(String info)
   {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
   }  

   public string MyProperty
   {
       set
       {
           if (_myProperty != value)
           {
               _myProperty = value;
               NotifyPropertyChanged("MyProperty");
           }
       }
   }
}

I get a nice green test from the following code in my unit tests, that uses delegates:

[TestMethod]
public void Test_ThatMyEventIsRaised()
{
    string actual = null;
    MyClass myClass = new MyClass();

    myClass.PropertyChanged += delegate(object sender, PropertyChangedEventArgs e)
    {
         actual = e.PropertyName;
    };

    myClass.MyProperty = "testing";
    Assert.IsNotNull(actual);
    Assert.AreEqual("MyProperty", actual);
}

However, if I then try and chain the setting of properties together like so:

public string MyProperty
{
    set
    {
        if (_myProperty != value)
        {
            _myProperty = value;
            NotifyPropertyChanged("MyProperty");
            MyOtherProperty = "SomeValue";
        }
    }
}

public string MyOtherProperty
{
    set
    {
        if (_myOtherProperty != value)
        {
            _myOtherProperty = value;
            NotifyPropertyChanged("MyOtherProperty");
        }
    }
}

My test for the event fails - the event that it captures is the event for the MyOtherProperty.

I'm pretty sure the event fires, my UI reacts like it does, but my delegate only captures the last event to fire.

So I'm wondering:
1. Is my method of testing events correct?
2. Is my method of raising chained events correct?


Everything you've done is correct, providing you want your test to ask "What is the last event that was raised?"

Your code is firing these two events, in this order

  • Property Changed (... "My Property" ...)
  • Property Changed (... "MyOtherProperty" ...)
  • Whether this is "correct" or not depends upon the purpose of these events.

    If you want to test the number of events that gets raised, and the order they get raised in, you can easily extend your existing test:

    [TestMethod]
    public void Test_ThatMyEventIsRaised()
    {
        List<string> receivedEvents = new List<string>();
        MyClass myClass = new MyClass();
    
        myClass.PropertyChanged += delegate(object sender, PropertyChangedEventArgs e)
        {
            receivedEvents.Add(e.PropertyName);
        };
    
        myClass.MyProperty = "testing";
        Assert.AreEqual(2, receivedEvents.Count);
        Assert.AreEqual("MyProperty", receivedEvents[0]);
        Assert.AreEqual("MyOtherProperty", receivedEvents[1]);
    }
    

    If you're doing TDD then event testing can start to generate a lot of repetitive code. I wrote an event monitor that enables a much cleaner approach to unit test writing for these situations.

    var publisher = new PropertyChangedEventPublisher();
    
    Action test = () =>
    {
        publisher.X = 1;
        publisher.Y = 2;
    };
    
    var expectedSequence = new[] { "X", "Y" };
    
    EventMonitor.Assert(test, publisher, expectedSequence);
    

    Please see my answer to the following for more details.

    Unit testing that an event is raised in C#, using reflection

    Or a series of blog articles I posted about it:

    http://gojisoft.com/blog/2010/04/22/event-sequence-unit-testing-part-1/


    This is very old and probably wont even be read but with some cool new .net features I have created an INPC Tracer class that allows that:

    [Test]
    public void Test_Notify_Property_Changed_Fired()
    {
        var p = new Project();
    
        var tracer = new INCPTracer();
    
        // One event
        tracer.With(p).CheckThat(() => p.Active = true).RaisedEvent(() => p.Active);
    
        // Two events in exact order
        tracer.With(p).CheckThat(() => p.Path = "test").RaisedEvent(() => p.Path).RaisedEvent(() => p.Active);
    }
    

    See gist: https://gist.github.com/Seikilos/6224204

    链接地址: http://www.djcxy.com/p/54050.html

    上一篇: 使用“发件人”与自定义EventArgs

    下一篇: 单元测试在C#中引发事件