2

我有一個簡單的類實現INotifyPropertyChanged,我調用另一個線程上的屬性更改,我很難讓FluentAsserts看到propertyChanged被調用。如果我在async Task方法中使用Task.Delay,似乎不會發生。但是,如果我只是在睡覺的話。Fluent-Assertions ShouldRaisePropertyChangeFor不適用於異步任務?

SimpleNotify類:

namespace FluentAssertPropertyThreads 
{ 
    class SimpleNotify : System.ComponentModel.INotifyPropertyChanged 
    { 
     public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 
     private void onChange(string name) 
     { 
      this.PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(name)); 
     } 

     private int count = 0; 
     public int Count 
     { 
      get 
      { 
       return this.count; 
      } 

      set 
      { 
       if (this.count != value) 
       { 
        this.count = value; 
        this.onChange(nameof(this.Count)); 
       } 
      } 
     } 
    } 
} 

,這裏是我的單元測試:

using FluentAssertions; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 
using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace FluentAssertPropertyThreads 
{ 
    [TestClass] 
    public class UnitTest1 
    { 
     private SimpleNotify simpleNotify; 
     private int notifyCount; 
     private void bumpCount() 
     { 
      this.simpleNotify.Count++; 
     } 

     private void SimpleNotify_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) 
     { 
      SimpleNotify simpleNotify = sender as SimpleNotify; 
      if (simpleNotify == null) 
      { 
       throw new ArgumentNullException(nameof(sender), "sender should be " + nameof(SimpleNotify)); 
      } 

      if (e.PropertyName.Equals(nameof(SimpleNotify.Count), StringComparison.InvariantCultureIgnoreCase)) 
      { 
       this.notifyCount++; 
      } 
     } 

     [TestInitialize] 
     public void TestSetup() 
     { 
      this.notifyCount = 0; 
      this.simpleNotify = new SimpleNotify(); 
      this.simpleNotify.PropertyChanged += SimpleNotify_PropertyChanged; 
      this.simpleNotify.MonitorEvents(); 

      Thread thread = new Thread(this.bumpCount) 
      { 
       IsBackground = true, 
       Name = @"My Background Thread", 
       Priority = ThreadPriority.Normal 
      }; 
      thread.Start(); 
     } 

     [TestMethod] 
     public async Task TestMethod1() 
     { 
      await Task.Delay(100); 
      this.notifyCount.Should().Be(1);  //this passes, so I know that my notification has be executed. 
      this.simpleNotify.ShouldRaisePropertyChangeFor(x => x.Count);  //but this fails, saying that I need to be monitoring the events (which I am above) 
     } 

     [TestMethod] 
     public void TestMethod2() 
     { 
      Thread.Sleep(100); 
      this.notifyCount.Should().Be(1);  //this passes, so I know that my notification has be executed. 
      this.simpleNotify.ShouldRaisePropertyChangeFor(x => x.Count);  //this passes as I expected 
     } 
    } 
} 

確切的錯誤是:

System.InvalidOperationException:對象沒有被用於監視事件或已經被垃圾收集。使用MonitorEvents()擴展方法開始監視事件。

我不明白如果我使用await或Thread.Sleep如何MonitorEvents會在意。我錯過了什麼?我得到那await離開的方法和回來,而Thread.Sleep不。

因此,當它在await期間離開TestMethod1時,它正在擊中FluentAsserts用於跟蹤屬性的對象上的配置?可以嗎?應該是?

回答

2

是的,事情就像你說的:await暫停當前方法的執行並創建一個狀態機,在Delay完成後返回該方法。但是調用者(一個UnitTest引擎)不希望你的測試是異步的,只是簡單地結束執行,這導致了對象的處理。

斯蒂芬·克利裏寫下輝煌MSDN article about Unit testing and async/await keywords,你應該出來將你的代碼,該方法返回Task並等待它的所有測試,這樣的事情:

async Task Testing() 
{ 
    await Task.Delay(100); 
    this.notifyCount.Should().Be(1); 
    this.simpleNotify.ShouldRaisePropertyChangeFor(x => x.Count); 
} 

[TestMethod] 
public async Task TestMethod1() 
{ 
    await Testing(); 
} 

但這仍可能失敗,因爲一次性處理後可執行await之後的邏輯。

0

看起來像5.0.0版本將包含對監測異步測試的支持。

Issue被提出,work completed只是等待文檔0123'和版本5.0.0被髮布。

但暫時沒有與代碼搶鮮改變5.0.0-beta2可以從預發佈版本得到它的NuGet

從更改日誌中:

{}破取代了舊thread-不安全的MonitorEvents API與新的 監視器擴展方法,該方法將返回一個線程安全監視 作用域,該作用域公開像Should()這樣的方法。提高()和元數據,如 OccurredEvents和MonitoredEvents

所以用更新的NuGet代碼將是這樣的:

[TestInitialize] 
public void TestSetup() 
{ 
    this.notifyCount = 0; 
    this.simpleNotify = new SimpleNotify(); 
    this.simpleNotify.PropertyChanged += SimpleNotify_PropertyChanged; 

    Thread thread = new Thread(this.bumpCount) 
    { 
     IsBackground = true, 
     Name = @"My Background Thread", 
     Priority = ThreadPriority.Normal 
    }; 
    thread.Start(); 
} 

[TestMethod] 
public async Task TestMethod1() 
{ 
    using (var MonitoredSimpleNotify = this.simpleNotify.Monitor()) 
    { 
     await Task.Delay(100); 
     this.notifyCount.Should().Be(1); 
     MonitoredSimpleNotify.Should().RaisePropertyChangeFor(x => x.Count); // New API for Monitoring 
    } 
}