一个单元如何测试基于过程或基于事件的代码部分

一个单元如何测试基于过程或基于事件的代码部分

How does one unit test sections of code that are procedural or event-based

我从这个演示文稿和网站上的其他评论中确信我需要学习单元测试。我也意识到这里有很多关于什么是单元测试的问题。每次我去考虑应该如何在我目前正在处理的应用程序中完成它时,我都会困惑地走开。它是一个xulrunner应用程序,很多逻辑都是基于事件的——当用户点击这里时,这个动作就会发生。

我看到的测试示例通常是测试类——它们实例化一个对象,给它模拟数据,然后检查对象的属性。这对我来说很有意义 - 但是非面向对象的部分呢?

这家伙提到基于 GUI 的单元测试在大多数测试框架中都很困难,也许这就是问题所在。上面链接的演示文稿提到,每个测试一次只能接触一个类,一种方法。这似乎排除了我正在尝试做的事情。

那么问题来了——如何对基于程序或基于事件的代码进行单元测试?提供指向良好文档的链接,或自行解释。

另外,我还面临一个挑战,即我没有找到用于测试 xulrunner 应用程序的测试框架 - 似乎这些工具还没有开发出来。我想这比我理解概念、编写可测试代码、应用单元测试更外围。


单元测试的理念是在每次测试中测试一小段代码。在基于事件的系统中,您可以进行的一种单元测试形式是测试您的事件处理程序如何响应各种事件。所以你的单元测试可能会将你程序的某个方面设置为特定的状态,然后直接调用事件监听方法,最后测试你程序的后续状态。

如果您计划对基于事件的系统进行单元测试,那么如果您使用依赖注入模式并且理想情况下会全程使用控制反转(参见 http:// martinfowler.com/articles/injection.html 和 http://msdn.microsoft.com/en-us/library/aa973811.aspx 了解这些模式的详细信息)

(感谢 pc1oad1etter 指出我弄乱了链接)


在这里回答我自己的问题,但我看到一篇文章解释了这个问题,并做了一个简单示例的演练——敏捷用户界面开发。代码和图像都很棒,下面是一个展示这个想法的Fragments:

Agile gurus such as Kent Beck and
David Astels suggest building the GUI
by keeping the view objects very thin,
and testing the layers"below the
surface." This"smart object/thin
view" model is analogous to the
familiar document-view and
client-server paradigms, but applies
to the development of individual GUI
elements. Separation of the content
and presentation improves the design
of the code, making it more modular
and testable. Each component of the
user interface is implemented as a
smart object, containing the
application behavior that should be
tested, but no GUI presentation code.
Each smart object has a corresponding
thin view class containing only
generic GUI behavior. With this design
model, GUI building becomes amenable
to TDD.


起初我会像这样测试事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private bool fired;

private void HandlesEvent(object sender, EventArgs e)
{
    fired = true;
 }

public void Test()
{
   class.FireEvent += HandlesEvent;
   class.PErformEventFiringAction(null, null);

   Assert.IsTrue(fired);
}

然后我发现了 RhinoMocks。
RhinoMocks 是一个创建模拟对象的框架,它还处理事件测试。它也可能对您的程序测试派上用场。


您的问题没有说明您选择的编程语言,但我的问题是 C#,所以我将使用它来举例说明。然而,这只是对 Gilligans 答案的改进,它使用匿名委托来内联您的测试代码。我完全赞成使测试尽可能可读,对我来说这意味着测试方法中的所有测试代码;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Arrange
var car = new Car();
string changedPropertyName ="";
car.PropertyChanged += delegate(object sender, PropertyChangedEventArgs e)
                        {
                           if (sender == car)
                                  changedPropertyName = e.PropertyName;
                        };

// Act
car.Model ="Volvo";

// Assert
Assert.AreEqual("Model", changedPropertyName,
   "The notification of a property change was not fired correctly.");

我在这里测试的类实现了 INotifyPropertyChanged 接口,因此只要属性的值发生变化,就应该引发 NotifyPropertyChanged 事件。


问题在于"基于事件的编程"将太多的逻辑链接到事件。这样一个系统的设计方式是应该有一个引发事件的子系统(并且您可以编写测试以确保以正确的顺序引发这些事件)。并且应该有另一个子系统只处理管理,比如说,表单的状态。您可以编写一个单元测试来验证给定正确的输入(即引发的事件),将表单状态设置为正确的值。

除此之外,从组件 1 引发并调用组件 2 行为的实际事件处理程序只是集成测试,可以由 QA 人员手动完成。


请参阅经常链接的有效使用旧代码。请参阅标题为"我的应用程序是所有 API 调用"和"我的项目不是面向对象的。如何进行安全更改?"的部分。

在 C/C 世界(我的经验)中,实践中的最佳解决方案是使用链接器"seam"并针对被测试函数调用的所有函数链接测试替身。这样您就不会更改任何遗留代码,但您仍然可以单独对其进行测试。


我发现对过程代码有用的一种方法是使用 TextTest。它与单元测试无关,但它可以帮助您进行自动化回归测试。这个想法是让您的应用程序编写一个日志,然后使用 texttest 比较更改前后的日志。


推荐阅读