c# – 单元测试async void事件处理程序
我在c#
winforms中实现了MVP(MVC)模式.
我的视图和演示者如下(没有所有的MVP胶水): public interface IExampleView { event EventHandler<EventArgs> SaveClicked; string Message {get; set; } } public partial class ExampleView : Form { public event EventHandler<EventArgs> SaveClicked; string Message { get { return txtMessage.Text; } set { txtMessage.Text = value; } } private void btnSave_Click(object sender,EventArgs e) { if (SaveClicked != null) SaveClicked.Invoke(sender,e); } } public class ExamplePresenter { public void OnLoad() { View.SaveClicked += View_SaveClicked; } private async void View_SaveClicked(object sender,EventArgs e) { await Task.Run(() => { // Do save }); View.Message = "Saved!" } 我正在使用MSTest进行单元测试,以及NSubstitute进行模拟.我想模拟视图中的按钮单击以测试控制器的View_SaveClicked代码,如下所示: [TestMethod] public void WhenSaveButtonClicked_ThenSaveMessageShouldBeShown() { // Arrange // Act View.SaveClicked += Raise.EventWith(new object(),new EventArgs()); // Assert Assert.AreEqual("Saved!",View.Message); } 我能够使用NSubstitute的Raise.EventWith成功提升View.SaveClicked.但问题是,在Presenter有时间保存消息并且Assert失败之前,代码会立即进入Assert. 我理解为什么会发生这种情况并且设法通过在Assert之前添加Thread.Sleep(500)来解决它,但这不太理想.我也可以更新我的视图来调用presenter.Save()方法,但我希望View尽可能地与Presenter无关. 所以我想知道我可以改进单元测试,以等待异步View_SaveClicked完成或更改View / Presenter代码,以便在这种情况下更容易进行单元测试. 有任何想法吗? 解决方法
由于您只关心单元测试,因此您可以使用自定义SynchronizationContext,它允许您检测async void方法的完成.
您可以使用我的 [TestMethod] public void WhenSaveButtonClicked_ThenSaveMessageShouldBeShown() { // Arrange AsyncContext.Run(() => { // Act View.SaveClicked += Raise.EventWith(new object(),new EventArgs()); }); // Assert Assert.AreEqual("Saved!",View.Message); } 但是,最好在你自己的代码中使用avoid 一种方法是替换所有EventHandler< T>.普通代表的事件,并通过等待调用: public Func<Object,EventArgs,Task> SaveClicked; private void btnSave_Click(object sender,EventArgs e) { if (SaveClicked != null) await SaveClicked(sender,e); } 如果你想要一个真实的事件,这不太漂亮,但是: public delegate Task AsyncEventHandler<T>(object sender,T e); public event AsyncEventHandler<EventArgs> SaveClicked; private void btnSave_Click(object sender,EventArgs e) { if (SaveClicked != null) await Task.WhenAll( SaveClicked.GetInvocationList().Cast<AsyncEventHandler<T>> .Select(x => x(sender,e))); } 使用此方法,任何同步事件处理程序都需要在处理程序的末尾返回Task.CompletedTask. 另一种方法是使用“延迟”扩展EventArgs.这也不是很好,但对于异步事件处理程序来说更为惯用. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |