【问题标题】:Fluent-ASsertions ShouldRaisePropertyChangeFor does not work for async Tasks?Fluent-ASsertions ShouldRaisePropertyChangeFor 不适用于异步任务?
【发布时间】:2017-04-15 05:42:21
【问题描述】:

我有一个实现INotifyPropertyChanged 的简单类,我在另一个线程上调用属性更改,我很难让FluentAsserts 看到propertyChanged 被调用。如果我在 async Task 方法中使用 Task.Delay 似乎不会发生这种情况。但如果我只是让线程休眠,它就可以了。

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() 扩展方法开始监控事件。

我不明白 MonitorEvents 会在意我使用 await 还是 Thread.Sleep。我错过了什么?我知道await 离开该方法并返回,而Thread.Sleep 没有。

所以当它在等待期间离开TestMethod1 时,它正在处理FluentAsserts 用来跟踪属性的对象?可以吗?应该是?

【问题讨论】:

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


    【解决方案1】:

    是的,事情就像你说的那样: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 之后的逻辑可能会在一次性处置后执行。

    【讨论】:

      【解决方案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
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2022-01-13
        • 2017-08-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-02
        • 1970-01-01
        • 2020-03-12
        • 1970-01-01
        相关资源
        最近更新 更多