首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >JavaScript web前端的测试驱动开发

JavaScript web前端的测试驱动开发
EN

Stack Overflow用户
提问于 2011-04-01 14:02:43
回答 6查看 5.2K关注 0票数 33

这听起来可能有点傻,但实际上我有点困惑如何处理web前端的JavaScript测试。就我而言,典型的三层架构如下所示:

  1. 数据库层
  2. 应用层
  3. 客户层

1在这个问题上没有任何关系。2包含所有程序逻辑(“业务逻辑”)3前端。

我为大多数项目做测试驱动的开发,但只为应用程序逻辑,而不是前端。这是因为在TDD中测试UI是困难的和不寻常的,而且通常没有完成。相反,所有应用程序逻辑都与UI分离,因此测试该逻辑非常简单。

三层体系结构支持这一点:我可以将后端设计为REST,这是由我的前端调用的。JS测试是如何适应的?对于典型的三层体系结构,JS (即客户机上的JS )测试没有多大意义,对吗?

更新:为了澄清我的问题,我已经将问题的措辞从“在web前端中测试JavaScript”改为“测试驱动的JavaScript web前端开发”。

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2011-04-20 03:41:50

请记住,单元测试的目的是:确保特定的代码模块以预期的方式对某些刺激做出反应。在JS中,很大一部分代码(除非您有一些生命周期框架,如Sencha或YUI)将直接操作DOM或进行远程调用。为了测试这些东西,您只需应用传统的依赖注入和模拟/顽固性的单元测试技术。这意味着您必须编写要进行单元测试以接受依赖结构模拟的每个函数或类。

jQuery通过允许将XML文档传递到所有遍历函数来支持这一点。而你通常会写

代码语言:javascript
复制
$(function() { $('.bright').css('color','yellow'); }

你会想要写

代码语言:javascript
复制
function processBright(scope) {
    // jQuery will do the following line automatically, but for sake of clarity:
    scope = scope || window.document;

    $('.bright',scope).css('color','yellow');
}

$(processBright);

注意,我们不仅从匿名函数中提取逻辑并给它命名,我们还使该函数接受一个作用域参数。当该值为null时,jQuery调用仍将正常工作。但是,我们现在有了一个用于注入模拟文档的向量,我们可以在调用函数之后检查它。单元测试看起来就像

代码语言:javascript
复制
function shouldSetColorYellowIfClassBright() {
    // arrange
    var testDoc = 
        $('<html><body><span id="a" class="bright">test</span></body></html>');

    // act
    processBright(testDoc);

    // assert
    if (testDoc.find('#a').css('color') != 'bright')
        throw TestFailed("Color property was not changed correctly.");
}

TestFailed可能如下所示:

代码语言:javascript
复制
function TestFailed(message) {
    this.message = message;
    this.name = "TestFailed";
}

这种情况与远程调用类似,不过,与其实际注入某种功能,您还可以使用掩蔽存根。假设你有这样的功能:

代码语言:javascript
复制
function makeRemoteCall(data, callback) {
    if (data.property == 'ok') 
        $.getJSON({url:'/someResource.json',callback:callback});
}

您可以这样测试它:

代码语言:javascript
复制
// test suite setup
var getJSON = $.getJSON;
var stubCalls = [];
$.getJSON = function(args) {
    stubCalls[stubCalls.length] = args.url;
}

// unit test 1
function shouldMakeRemoteCallWithOkProperty() {
    // arrange
    var arg = { property: 'ok' };

    // act
    makeRemoteCall(arg);

    // assert
    if (stubCalls.length != 1 || stubCalls[0] != '/someResource.json')
        throw TestFailed("someResource.json was not requested once and only once.");
}

// unit test 2
function shouldNotMakeRemoteCallWithoutOkProperty() {
    // arrange
    var arg = { property: 'foobar' };

    // act
    makeRemoteCall(arg);

    // assert
    if (stubCalls.length != 0)
        throw TestFailed(stubCalls[0] + " was called unexpectedly.");
}

// test suite teardown
$.getJSON = getJSON;

(您可以将整个过程封装在模块模式中,以避免丢弃全局命名空间。)

要以测试驱动的方式应用所有这些,只需先编写这些测试。这是一种直截了当的,没有任何装饰,最重要的是,有效的方法单元测试JS。

像qUnit这样的框架可以用来驱动单元测试,但这只是问题的一小部分。您的代码必须以测试友好的方式编写。此外,Selenium、HtmlUnit、jsTestDriver或Watir/N等框架用于集成测试,而不是单元测试本身。最后,您的代码绝不必须是面向对象的。单元测试的原理很容易与面向对象系统中单元测试的实际应用相混淆。它们是相互独立但相互兼容的思想。

测试样式

我应该注意,这里演示了两种不同的测试风格。第一个假设完全不了解processBright的实现。它可以使用jQuery添加颜色样式,也可以使用本机DOM操作。我只是在测试函数的外部行为是否与预期的一样。在第二部分中,我假设了解函数的内部依赖关系(即$.getJSON),这些测试涵盖了与该依赖项的正确交互。

您采取的方法取决于您的测试哲学和总体优先级和成本效益的情况。第一个测试是相对纯净的。第二个测试很简单,但相对脆弱;如果我更改makeRemoteCall的实现,测试就会中断。最好是,makeRemoteCall使用$.getJSON的假设至少通过makeRemoteCall的文档来证明是正确的。还有几种更严格的方法,但有一种成本效益高的方法是将依赖关系封装在包装器函数中。代码库将只依赖于这些包装器,它们的实现可以很容易地在测试时用测试存根替换。

票数 24
EN

Stack Overflow用户

发布于 2011-04-13 12:52:11

有一本书名为“测试驱动的JavaScript开发”,作者是克里斯蒂安·约翰森,这本书可能对你有帮助。我只看过书中的一些样本(前几天刚下载了一个样本来点燃),但它看起来是一本很好的书,解决了这个问题。你可以去看看。

(注:我与克里斯蒂安·约翰森没有关系,也没有投资于这本书的销售。解决这个问题看上去是件好事。)

票数 3
EN

Stack Overflow用户

发布于 2011-04-13 12:43:05

我有一个与JS客户端层类似的架构应用程序。在我的例子中,我使用我们公司自己的JS框架来实现客户层。

这个JS框架是以面向对象的方式创建的,因此我可以为核心类和组件实现单元测试。此外,为了涵盖所有的用户交互(不能用单元测试覆盖),我正在使用硒WebDriver对框架可视化组件进行集成测试,并在不同的浏览器中测试它们。

因此,如果测试代码是以面向对象的方式编写的,那么TDD就可以应用到JavaScript开发中。此外,集成测试也是可能的(并且可以用来进行某种类型的TDD)。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5514466

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档