2008-10-30 4 views
137

Tôi có một số mã tăng PropertyChanged sự kiện và tôi muốn có thể kiểm tra đơn vị rằng các sự kiện đang được nâng lên chính xác.Kiểm tra đơn vị sự kiện được nêu trong C#

Các mã được nâng cao sự kiện là như

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

tôi nhận được một bài kiểm tra xanh đẹp từ đoạn mã sau trong các thử nghiệm đơn vị của tôi, sử dụng các đại biểu:

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

Tuy nhiên, nếu tôi sau đó thử và chuỗi cài đặt các thuộc tính với nhau như vậy:

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

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

Thử nghiệm của tôi cho sự kiện không thành công - sự kiện mà nó chụp là sự kiện cho MyOtherProperty.

Tôi chắc chắn rằng sự kiện đã xảy ra, giao diện người dùng của tôi phản ứng như vậy, nhưng đại biểu của tôi chỉ chụp sự kiện cuối cùng để kích hoạt.

Vì vậy, tôi tự hỏi:
1. Phương pháp thử nghiệm của tôi có đúng không?
2. Phương pháp nâng cao bị xích sự kiện có đúng không?

Trả lời

155

Mọi thứ bạn đã làm là chính xác, cung cấp cho bạn muốn thử nghiệm của bạn hỏi "Sự kiện cuối cùng được nêu ra là gì?"

Mã của bạn là bắn hai sự kiện này, theo thứ tự này

  • tài sản thay đổi (... "My Property" ...)
  • tài sản thay đổi (... "MyOtherProperty" ...)

Cho dù điều này "đúng" hay không phụ thuộc vào mục đích của những sự kiện này.

Nếu bạn muốn kiểm tra số lượng sự kiện đó được nâng lên, và theo thứ tự chúng được lớn lên tại, bạn có thể dễ dàng mở rộng thử nghiệm hiện tại của bạn:

[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]); 
} 
+9

Phiên bản ngắn hơn: myClass.PropertyChanged + = (đối tượng người gửi, e) => receivedEvents.Add (e.PropertyName); – ShloEmi

18

Nếu bạn đang làm TDD sau đó thử nghiệm sự kiện có thể bắt đầu tạo một mã số lặp lại. Tôi đã viết một trình giám sát sự kiện cho phép một cách tiếp cận rõ ràng hơn để viết bài kiểm tra đơn vị cho các tình huống này.

var publisher = new PropertyChangedEventPublisher(); 

Action test =() => 
{ 
    publisher.X = 1; 
    publisher.Y = 2; 
}; 

var expectedSequence = new[] { "X", "Y" }; 

EventMonitor.Assert(test, publisher, expectedSequence); 

Vui lòng xem câu trả lời sau để biết thêm chi tiết.

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

Hoặc một loạt các bài báo viết blog Tôi gửi về nó:

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

+1

Liên kết thứ hai bị hỏng. – Lennart

5

Dưới đây là một chút thay đổi mã Andrew đó thay vì chỉ đăng nhập chuỗi các sự kiện nêu ra chứ không phải đếm bao nhiêu lần một sự kiện cụ thể đã được gọi. Mặc dù nó dựa trên mã của anh ấy nhưng tôi thấy nó hữu ích hơn trong các bài kiểm tra của tôi.

[TestMethod] 
public void Test_ThatMyEventIsRaised() 
{ 
    Dictionary<string, int> receivedEvents = new Dictionary<string, int>(); 
    MyClass myClass = new MyClass(); 

    myClass.PropertyChanged += delegate(object sender, PropertyChangedEventArgs e) 
    { 
     if (receivedEvents.ContainsKey(e.PropertyName)) 
      receivedEvents[e.PropertyName]++; 
     else 
      receivedEvents.Add(e.PropertyName, 1); 
    }; 

    myClass.MyProperty = "testing"; 
    Assert.IsTrue(receivedEvents.ContainsKey("MyProperty")); 
    Assert.AreEqual(1, receivedEvents["MyProperty"]); 
    Assert.IsTrue(receivedEvents.ContainsKey("MyOtherProperty")); 
    Assert.AreEqual(1, receivedEvents["MyOtherProperty"]); 
} 
7

Đây là rất cũ và có lẽ thậm chí wont được đọc nhưng với một số tính năng mới .net mát Tôi đã tạo ra một lớp INPC Tracer cho phép rằng:

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

Xem ý chính: https://gist.github.com/Seikilos/6224204

+0

Đẹp - bạn nên xem xét đóng gói nó lên và xuất bản nó trên nuget.org –

0

Dựa trên bài viết này, tôi đã tạo ra trình trợ giúp xác nhận đơn giản này:

private void AssertPropertyChanged<T>(T instance, Action<T> actionPropertySetter, string expectedPropertyName) where T : INotifyPropertyChanged 
    { 
     string actual = null; 
     instance.PropertyChanged += delegate (object sender, PropertyChangedEventArgs e) 
     { 
      actual = e.PropertyName; 
     }; 
     actionPropertySetter.Invoke(instance); 
     Assert.IsNotNull(actual); 
     Assert.AreEqual(propertyName, actual); 
    } 

Với phương pháp trợ giúp này, kiểm tra trở nên thực sự đơn giản.

[TestMethod()] 
public void Event_UserName_PropertyChangedWillBeFired() 
{ 
    var user = new User(); 
    AssertPropertyChanged(user, (x) => x.UserName = "Bob", "UserName"); 
} 
0

Đừng viết một bài kiểm tra cho mỗi thành viên - đây là nhiều việc

(có lẽ giải pháp này không phải là hoàn hảo cho mọi tình huống -. Nhưng nó cho thấy một cách tốt Bạn có thể cần phải thích ứng với nó cho trường hợp sử dụng của bạn)

có thể sử dụng phản ánh trong một thư viện để kiểm tra nếu các thành viên của bạn đều được đáp ứng để thay đổi sự kiện bất động sản của bạn một cách chính xác:

  • PropertyChanged sự kiện được nâng lên trên sette r truy cập
  • tổ chức sự kiện được nêu ra chính xác (tên của tài sản tương đương với đối số của sự kiện nêu ra)

Các mã sau đây có thể được sử dụng một thư viện cho thấy làm thế nào để kiểm tra các lớp gernic

using System.ComponentModel; 
using System.Linq; 

/// <summary> 
/// Check if every property respons to INotifyPropertyChanged with the correct property name 
/// </summary> 
public static class NotificationTester 
    { 
     public static object GetPropertyValue(object src, string propName) 
     { 
      return src.GetType().GetProperty(propName).GetValue(src, null); 
     } 

     public static bool Verify<T>(T inputClass) where T : INotifyPropertyChanged 
     { 
      var properties = inputClass.GetType().GetProperties().Where(x => x.CanWrite); 
      var index = 0; 

      var matchedName = 0; 
      inputClass.PropertyChanged += (o, e) => 
      { 
       if (properties.ElementAt(index).Name == e.PropertyName) 
       { 
        matchedName++; 
       } 

       index++; 
      }; 

      foreach (var item in properties) 
      { 
       // use setter of property 
       item.SetValue(inputClass, GetPropertyValue(inputClass, item.Name)); 
      } 

      return matchedName == properties.Count(); 
     } 
    } 

Các thử nghiệm của lớp học của bạn bây giờ có thể được viết như. (Có lẽ bạn muốn chia bài kiểm tra vào "sự kiện là có" và "sự kiện lớn lên với tên chính xác" - bạn có thể làm điều này cho mình)

[TestMethod] 
public void EveryWriteablePropertyImpementsINotifyPropertyChangedCorrect() 
{ 
    var viewModel = new TestMyClassWithINotifyPropertyChangedInterface(); 
    Assert.AreEqual(true, NotificationTester.Verify(viewModel)); 
} 

Lớp

using System.ComponentModel; 

public class TestMyClassWithINotifyPropertyChangedInterface : INotifyPropertyChanged 
{ 
     public event PropertyChangedEventHandler PropertyChanged; 

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

     private int id; 

     public int Id 
     { 
      get { return id; } 
      set { id = value; 
       NotifyPropertyChanged("Id"); 
      } 
     } 
} 
+0

Tôi đã thử điều này, nhưng nếu định cư tài sản của tôi có một tuyên bố bảo vệ như "if (value == _myValue) return", mà tất cả tôi làm, sau đó ở trên sẽ không hoạt động, trừ khi tôi đang thiếu một cái gì đó. Tôi vừa mới đến từ C++ đến C#. – codah

0

Tôi đã thực hiện một phần mở rộng ở đây:

public static class NotifyPropertyChangedExtensions 
{ 
    private static bool _isFired = false; 
    private static string _propertyName; 

    public static void NotifyPropertyChangedVerificationSettingUp(this INotifyPropertyChanged notifyPropertyChanged, 
     string propertyName) 
    { 
     _isFired = false; 
     _propertyName = propertyName; 
     notifyPropertyChanged.PropertyChanged += OnPropertyChanged; 
    } 

    private static void OnPropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     if (e.PropertyName == _propertyName) 
     { 
      _isFired = true; 
     } 
    } 

    public static bool IsNotifyPropertyChangedFired(this INotifyPropertyChanged notifyPropertyChanged) 
    { 
     _propertyName = null; 
     notifyPropertyChanged.PropertyChanged -= OnPropertyChanged; 
     return _isFired; 
    } 
} 

có việc sử dụng:

[Fact] 
    public void FilesRenameViewModel_Rename_Apply_Execute_Verify_NotifyPropertyChanged_If_Succeeded_Through_Extension_Test() 
    { 
     // Arrange 
     _filesViewModel.FolderPath = ConstFolderFakeName; 
     _filesViewModel.OldNameToReplace = "Testing"; 
     //After the command's execution OnPropertyChanged for _filesViewModel.AllFilesFiltered should be raised 
     _filesViewModel.NotifyPropertyChangedVerificationSettingUp(nameof(_filesViewModel.AllFilesFiltered)); 
     //Act 
     _filesViewModel.ApplyRenamingCommand.Execute(null); 
     // Assert 
     Assert.True(_filesViewModel.IsNotifyPropertyChangedFired()); 

    }