Fluent-ASertions ShouldRaisePropertyChangeFor 不适用于异步任务?
Fluent-ASsertions ShouldRaisePropertyChangeFor does not work for async Tasks?
我有一个实现 INotifyPropertyChanged
的简单 class,我在另一个线程上调用 属性 更改,我很难 FluentAsserts
看到propertyChanged
被调用了。如果我在 async Task
方法中使用 Task.Delay
似乎不会发生。但如果我只是让线程休眠,它就会起作用。
SimpleNotify
class:
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: Object is not being monitored for events or has already been garbage collected. Use the MonitorEvents() extension method to start monitoring events.
我不明白 MonitorEvents
会怎么关心我是使用 await 还是 Thread.Sleep
。我错过了什么?我知道 await
离开方法并返回,而 Thread.Sleep
没有。
所以当它在等待期间离开 TestMethod1
时,它正在对 FluentAsserts
用来跟踪属性的对象进行处置?可以吗?应该是?
是的,事情就像你说的: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
之后的逻辑可能会在处理完一次性用品后执行。
看起来 5.0.0 版将包括对带监控的异步测试的支持。
此Issue was raised and the work completed just waiting on the documentation to be updated及5.0.0版本即将发布
但目前有代码更改的预发布5.0.0-beta2可以从 NuGet 上的预发布版本中获取
来自更改日志:
{Breaking} Replaced the old thread-unsafe MonitorEvents API with a new
Monitor extension method that will return a thread-safe monitoring
scope that exposes methods like Should().Raise() and metadata such as
OccurredEvents and 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
}
}
我有一个实现 INotifyPropertyChanged
的简单 class,我在另一个线程上调用 属性 更改,我很难 FluentAsserts
看到propertyChanged
被调用了。如果我在 async Task
方法中使用 Task.Delay
似乎不会发生。但如果我只是让线程休眠,它就会起作用。
SimpleNotify
class:
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: Object is not being monitored for events or has already been garbage collected. Use the MonitorEvents() extension method to start monitoring events.
我不明白 MonitorEvents
会怎么关心我是使用 await 还是 Thread.Sleep
。我错过了什么?我知道 await
离开方法并返回,而 Thread.Sleep
没有。
所以当它在等待期间离开 TestMethod1
时,它正在对 FluentAsserts
用来跟踪属性的对象进行处置?可以吗?应该是?
是的,事情就像你说的: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
之后的逻辑可能会在处理完一次性用品后执行。
看起来 5.0.0 版将包括对带监控的异步测试的支持。
此Issue was raised and the work completed just waiting on the documentation to be updated及5.0.0版本即将发布
但目前有代码更改的预发布5.0.0-beta2可以从 NuGet 上的预发布版本中获取
来自更改日志:
{Breaking} Replaced the old thread-unsafe MonitorEvents API with a new Monitor extension method that will return a thread-safe monitoring scope that exposes methods like Should().Raise() and metadata such as OccurredEvents and 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
}
}