YUI的测试编写有效的JavaScript单元测试

2009年1月5日,10:38上午由Nicholas C. Zakas | 开发 | 6评论

其中的-“的雷达在JavaScript开发的动作下在2008年最大的是,单元测试的兴趣重现。 YUI的测试 ,YUI的单元测试框架,达成在二月遗传状态和其他库或者介绍他们自己的单元测试框架或开始宣传现有的。 因此,有很多关于建立为JavaScript单元测试文件。 简单的JavaScript单元测试是不足够,但如果你的测试被写入不当,它们能导致很多失去的时间。 学习编写有效的JavaScript单元测试会为您节省时间和头痛,在未来。

您正在测试什么?

编写有效的单元测试的关键是要了解守信用“单位。” 在测试方面,一个单位是一个孤立的部分代码,可以独立于其他部分的代码测试。 在一个像JavaScript的面向对象的语言,每一种方法被认为是一个单位。 正确的面向对象的设计通常需要很好地封装为一个单一的目的,因此容易测试的方法。

传统的单元测试的目的是测试接口实现的,因此,私有方法没有得到明确的测试。 这就是所谓的黑盒测试。 我们的想法是,你可以换出接口实现和单元测试将仍然通过,因为他们是完全无关的底层实现。 所有的测试知道的是一组必须满足的约束,他们并不关心如何满足这些约束。

编写测试

正如我在我的谈话中说,单元测试应该测试输入和输出。 输入可以被命名方法参数或在全球范围内访问变量的变化,该方法取决于正常。 输出可以是返回值,状态变量的变化,甚至抛出的错误。 对于每一个输入输出设置,应该有一个单一的单元测试。 每个测试都应该明确说明,“这些投入,我希望这些产出。” 任何偏离这一声明是一个失败的测试。

每个测试应尽可能和测试只有一个输入输出设置简单,组合成一个单一的测试集最小化的单元测试的有效性。 例如,考虑下面的测试,称为函数trim()

 VAR的TestCase =新YAHOO.tool.TestCase({
    名称:“TRIM()测试”,
     testTrim:函数(){
         RESULT1 = TRIM(“世界您好”);
         YAHOO.util.Assert.areEqual(“世界您好”,RESULT1,“前导空格应该被剥夺。”);
         RESULT2 = TRIM(“世界您好”);
         YAHOO.util.Assert.areEqual(“你好世界”,RESULT2,“尾随空格应该被剥夺。”);
     }
 }); 

在这里,测试用例的testTrim()方法实际上是测试两个不同的输入输出集:

  1. 输入字符串的前导空格;返回值没有前导空白。
  2. 输入字符串结尾的空白;返回值没有尾随空格。

问题是,这两套字面上彼此没有关系,但如果第一组输入输出不能产生正确的结果,第二组将永远不会被测试。 这是其中一个失败口罩另一个情况。 这是更有效地分离出这些输入输出集分为两个测试:

  VAR的TestCase =新YAHOO.tool.TestCase({
    名称:“TRIM()测试”,
     testTrimWithLeadingWhiteSpace:函数(){
        结果= TRIM(“世界您好”);
         YAHOO.util.Assert.areEqual(“你好世界”,结果,“前导空格应该被剥夺。”);
     },
     testTrimWithTrailingWhiteSpace:函数(){
        结果= TRIM(“世界您好”);
         YAHOO.util.Assert.areEqual(“世界您好”,结果,“尾随空格应该被剥夺。”);
     }
 }); 

此代码现在可以正确地测试trim()函数的输入输出集,使它们分开。

总是写单元测试,如果被测试的代码可以正常工作。 良好的软件设计包括制订这些输入输出设置为提前让你知道在各种情况下的结果应该是什么。 在这种方式,单元测试,成为除了实际的代码中的技术要求文件类型。

有效的断言

编写单元测试的最重要部分之一,是正确的断言定义。 每个断言指定一个条件,如果不符合要求,指示,功能不恰当的行为。 重要的是要使用尽可能多的说法,正确的测试代码的输出。 太多的断言可导致假故障,而过少可导致假通行证。

在前面的例子中,每个测试包含一个单一的断言,因为这是所有的需要。 我确切地知道,要返回,所以我专门测试值。 测试可能看起来很简单,但他们把工作做好。 再次,没有规则的断言,使一个很好的测试数量,只是确保你测试每一个代码的预期给定的输入输出。

为了使测试失败更加连贯,你应该包括与每一个断言失败的消息。 在YUI的测试,这始终是最后一个参数的任何断言方法。 失败消息应该告诉你什么,不应该发生什么情况。 一些例子:

 / /错误失败的消息
 YAHOO.util.Assert.areEqual(“你好世界”,结果,“其结果是不是”世界你好“”);

 / /好失败的消息
 YAHOO.util.Assert.areEqual(“你好世界”,结果,“前导空格应该被剥夺。”);

注意坏事,也是好事失败消息之间的差别:不好告诉你发生了什么,并告诉你所预期的。 运行测试时,失败已经表明,发生了一些意料之外的,所以没有必要简单地重复,发生了一些意料之外的。 它更有助于了解应该发生什么,因为它是您的要求精确地表示。 通过采取这种办法,故障最终被一个没有兑现的要求,你可以回去和评估列表。

使用DOM

JavaScript是独一无二的,因为它经常有环境,DOM关系的其他语言。 很大程度上与DOM交互的方法,单元测试是很难的,因为整个环境必须按顺序设置的方法完全执行。 更加复杂的问题是由用户操作,如鼠标点击触发JavaScript的倾向。 YUI的测试提供事件模拟,以帮助创建测试方法是依赖于DOM的互动,但是,这开始越境进入功能测试区。

反对单元测试,功能测试,旨在测试产品用户的体验,而不是代码的输入输出集。 如果你发现自己想测试的用户界面,在一种特定的方式,由于用户交互响应,那么你真的要来写,而不是一些单元测试,功能测试。 YUI的测试可以用来写一些基本的功能测试,但这样的测试工具(相当不错)最流行 ​​的是

确定的东西,如果是一个单元测试的最佳途径是问如果可以前写的代码,它的设计测试确实存在。 单元测试,测试驱动开发的一部分,实际上是应该被写入实际的代码,作为一种方法来指导发展的努力。 另一方面,功能测试,不存在提前因为他们绑到用户界面和如何响应用户交互更改。

构建测试层次结构

YUI的测试,就像其他单元测试框架,支持层次的测试用例和测试套件。 每个测试套件可以包含其他的测试套件,以及测试用例;只有测试用例可以包含实际测试(与单词“test”开始的方法)。 最好的方式来组织测试层次结构是遵循一个非常简单的模式:

  • 你要测试的每个对象创建一个测试套件。
  • 每一个对象,你要测试,并把它添加到对象的测试套件的方法创建一个测试用例。
  • 创建一个为每个输入输出设置在每个测试用例的测试。

在这种方式,您的测试层次反映你正在测试的代码,它很容易找出其中应建立新的考验。

运行测试!

也许最重要的部分单元测试是经常运行测试。 时定期进行的测试是唯一有效的。 至少,你应该运行单元测试之前检查源代码控制的变化。 优化,你还定期运行自动测试,以验证任何更改后,他们一直致力于源头控制。 这是您将如何得到单元测试最大的好处是:快速鉴别力,并希望预防回归。

进一步的信息

共享和扩展: 书签del.icio.us Digg它! | reddit!

6评论

  1. 锐的自动运行定期的测试? 如果是这样,你可以分享你如何做到这一点的任何信息? 我特别感兴趣的是你如何处理与产卵和关闭用于在测试运行过程中自动构建浏览器的处理,以及如何收集和汇总,为构建测试结果。

    西蒙- 1月5日,2009

  2. 我已经看到了thusfar最好的解决办法是使用Selenium测试浏览器的生命周期管理。 你可以将它设置为定期运行,然后监视结果的页面。

    Nicholas C. Zakas - 1月7日,2009

  3. 首先,感谢写这美妙的博客条目。 我想以下评论:

    在一个像JavaScript的面向对象的语言,每一种方法被认为是一个单位

    个人比较喜欢一类作为一个单位的思想。 这个类提供了我的一些行为,我想通过我的测试,来解释行为。 在方法级别,我们似乎是执行在某种意义上钻。

    单元测试,测试输入和输出

    我宁愿如果你的资格你状态的单元测试,单元测试的风格。 有一个不同的学校,相信在互动为基础的测试单元测试。 如果没有返回值,状态变量的变化,或抛出错误的输出。 其重点是与它的合作者(方法调用)的相互作用。

    在YUI的试验,失败的消息始终是最后一个参数的任何断言方法

    他们似乎已经打破的xUnit公约。 xUnit的失败的消息始终是第一个参数。

    你声称:

    //Bad failure message
    YAHOO.util.Assert.areEqual("Hello world", result, "The result wasn't 'Hello world'");

    / /好失败的消息
    YAHOO.util.Assert.areEqual(“你好世界”,结果,“前导空格应该被剥夺。”);

    虽然我同意你的第二个例子是,比第一次更好,但我认为失败的消息在第二种情况下是多余的。 如果你看看下面整个的测试方法,

    testTrimWithLeadingWhiteSpace: function(){
    var result = trim(" Hello world");
    YAHOO.util.Assert.areEqual("Hello world", result, "Leading white space should be stripped.");
    }

    方法的名字已经告诉你,前导空格应修剪或剥离出来。 在这种特定的情况下,我不会放弃任何失败消息。 如果给贵公司的最佳实践之一,那么我宁愿喜欢你的失败消息“前导空格不被剥夺”

    另一方面,功能测试,不存在提前因为他们绑到用户界面和如何响应用户交互更改。

    这并非总是如此。 在很多情况下,实际上你可以写你的功能测试或验收测试之前创建的任何代码。 参照验收测试驱动开发

    你要测试的每个对象创建一个测试套件

    为什么这很重要吗? 我不知道这将如何帮助?

    每一个对象,你要测试,并把它添加到对象的测试套件的方法创建一个测试用例。

    我宁愿在我的生产代码中创建测试类的每个重要的实体和行为的类满足每写一个测试方法。 有时,我可能有超过1测试的一种方法,有时没有。 我不知道如果我想创建一个指引,说每一个方法都应该有一个测试。

    评论由纳雷什耆那教 - ,2009年1月11日

  4. 您好纳雷什

    感谢您有见地的意见。 事实上,有许多学校周围的测试方法的思想,你对其中一些感动。 我只是想澄清几个点。

    使断言失败消息应发生的声明是不是多余的,这是刚刚发生了什么解释。 测试的名称和失败的消息可能是相似的,在这种情况下,因为测试是如此简单,但是,他们可能是非常不同的,如果有一些在测试中的断言。

    我建议设立测试套件和测试用例是让人们开始。 我不是说每一个方法都应该有一个测试,我说的每一个方法,应该有一个测试用例包含的试验,探索每个输入输出设置,你有。 这一点是有一个逻辑结构,地图直接向你编程的对象。

    我希望这个澄清一些点;感谢您的意见。

    Nicholas C. Zakas - ,2009年1月12日

  5. 您好尼古拉斯
    我最近开始探索现有的门户网站上yuitest JS代码进行单元测试,我们继承的支持和已经使用了一些YUI库2。 你已经提出了博客的所有视频和感谢。
    1。 有例子/ yuitest样品更实际/现实的情况下使用不同的背景,广泛的DOM usgae等yuitest网站上的例子一样,是非常简单的。
    2。 我之所以问是因为网页上的内容一般是动态的,是基于对一些先决条件......容器元素是相同的,但内容的变化。 到了每个场景的不同htmls存根是最好吗?
    3。 有可能是大量使用DOM的一些脚本。 应该像原来的HTML整个HTML在这种情况下,被掐灭。

    评论kaanta - 12月8日,2010

  6. 您好Kaanta

    目前所有的例子,我可以分享文档中包含的YUI下载(ZIP文件中,检查测试目录)的一部分。

    如果你在页面上测试了很多的互动,然后我通常会建议到实际的页面加载YUI的测试和运行测试。 这样,您不必担心存根出某件东西的工作页面。

    Nicholas C. Zakas - 12月8日,2010

很抱歉,评论已被封闭,在这个时候。

主办雅虎

©2006-2012雅虎公司所有权利保留。 隐私政策 - 服务条款

支持WordPress的关于雅虎 虚拟主机