Fluent-ASsertions ShouldRaisePropertyChangeFor不适用于异步任务?

时间:2016-11-30 19:02:53

标签: c# multithreading thread-safety async-await fluent-assertions

我有一个实现library(formattable) formattable(table, list(groupA = color_tile("white", "orange")) ) 的简单类,我在另一个线程上调用属性更改,我很难让Dim currentChar = CInt(Mid(InputBox.Text, i, 1)) If currentChar <> 0 And currentChar <> 1 Then MessageBox.Show("Value Not Binary", "ERROR", MessageBoxButtons.OK) InputBox.Text = "" GoTo skipbin End If 看到INotifyPropertyChanged被调用。如果我在FluentAsserts方法中使用propertyChanged,似乎不会发生这种情况。但是如果我只是睡觉那就行了。

Task.Delay类:

async Task

这是我的单元测试:

SimpleNotify

确切的错误是:

  

System.InvalidOperationException:未监视对象的事件或已对垃圾进行垃圾回收。使用MonitorEvents()扩展方法开始监视事件。

如果我使用等待或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 } } } 如何关心。我错过了什么?我得到MonitorEvents离开方法并返回,而Thread.Sleep则没有。

因此,当它在等待期间离开await时,它正在对Thread.Sleep用于跟踪属性的对象进行处置?可以吗?应该是?

2 个答案:

答案 0 :(得分:2)

是的,事情就像你说的那样:await暂停当前方法的执行,并创建一个状态机,在Delay完成后返回方法。但是调用者(一个UnitTest引擎)不希望你的测试是异步的,只是结束执行,这导致了对象的处理。

Stephen Cleary写了一篇精彩的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之后的逻辑可能会在丢弃一次性后执行。

答案 1 :(得分:2)

看起来5.0.0版将包含对带监控的异步测试的支持。

Issue已被提出,而work completed只是等待文档updated和5.0.0版即将发布。

但目前有一个预发行代码,代码更改5.0.0-beta2可以从NuGet上的预发布版本中获取

从更改日志中

  

{Breaking}用新的替换旧的线程不安全的MonitorEvents API   监视将返回线程安全监视的扩展方法   暴露像Should()。Raise()和元数据等方法的范围   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
    }
}