我的视图和演示者如下(没有所有的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代码,以便在这种情况下更容易进行单元测试.
有任何想法吗?
解决方法
您可以使用我的AsyncContext
type:
[TestMethod] public void WhenSaveButtonClicked_ThenSaveMessageShouldBeShown() { // Arrange AsyncContext.Run(() => { // Act View.SaveClicked += Raise.EventWith(new object(),new EventArgs()); }); // Assert Assert.AreEqual("Saved!",View.Message); }
但是,最好在你自己的代码中使用avoid async void
(正如我在关于异步最佳实践的MSDN文章中所描述的那样).我有一篇博文,专门介绍了“async event handlers”的一些方法.
一种方法是替换所有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.这也不是很好,但对于异步事件处理程序来说更为惯用.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。