如何对基于UI的事件进行单元测试?

问题描述:

我想用MSTest编写单元测试,验证某个事件在控件元素被更改后被调用。不幸的是,对于comboBoxComPort(它是一个WinForms控件)事件处理程序“comboBoxComPort_SelectedIndexChanged”下面的代码永远不会被调用。这当然会导致测试代码中的匿名处理程序不被调用。如何对基于UI的事件进行单元测试?

[TestMethod()] 
public void ComboBoxComPort_SelectionChanged_DirtyEventFired() 
{ 
    ConfigUI target = new ConfigUI(); 
    var accessibleTarget = new PrivateObject(target); 
    ComboBox comboBoxComPort = (ComboBox)accessibleTarget.GetField("comboBoxComPort"); 

    bool dirtyEventCalled = false; 
    target.DirtyEvent += delegate 
    { 
     dirtyEventCalled = true; 
    }; 

    comboBoxComPort.SelectedIndex = comboBoxComPort.Items.Count - 1; 

    Assert.IsTrue(dirtyEventCalled); 
} 

为了简单起见,我们假设这是在“的SelectedIndexChanged” -handler:

public class ConfigUI 
{ 
    [...] 

    private void comboBoxComPort_SelectedIndexChanged(object sender, EventArgs e) 
    { 
     DirtyEvent(); 
    } 
} 

任何人能告诉我,为什么测试失败?我知道,在单元测试中访问私有成员通常不是一个好主意,但我没有看到测试UI行为的更好方法。对此的建议当然也是受欢迎的。

这是您的注册处理程序吗?

target.DirtyEvent += delegate 
{ 
    dirtyEventCalled = true; 
}; 

如果事件确实提高,但这并不意味着,当是不允许的事件“泡沫或隧道” ......根据不同的情境,事件可以被认为是在处理时的处理程序将被调用第一个处理程序接收控件。 WPF对这个概念特别挑剔,迫使处理程序将ISHandled位设置为false,以便事件在其他位置浮动。

如果可以的话;在事件的第一个处理程序中设置断点,然后在该事件的测试中设置断点。重新启动应用程序,并确保第一个处理程序首先看到它。在退出之前查看该处理程序中的事件数据。在第一个处理程序退出时,如果你的测试处理程序没有被调用,那是因为事件被认为是“满意的”。这应该让你更接近找到解决方案。

+0

感谢您的回复,我不完全确定我得到你的意思是“第一处理程序”。我试着调试代码,基本问题是“comboBoxComPort_SelectedIndexChanged”没有被调用(通过在那里设置一个断点来验证),这在我的眼中应该发生在线 comboBoxComPort.SelectedIndex = comboBoxComPort.Items.Count - 1之后; 另外我正在使用WinForms。 – flix 2014-10-31 14:03:35

+0

查看事件的第一个处理程序将是首先注册的处理程序,在应用程序代码(而不是测试代码)中设置断点(如果可以的话),然后遵循接下来会发生的事情。 – 2014-10-31 14:59:33

我会介绍一个ConfigUI类的接口(因为你想看到一个控件调用该类上的方法,对吧?),然后使用Moq库在测试中模拟ConfigUI并使用Moq.Verify看到该方法被调用(你可以要求验证说多少次,等等......)