Herylee

2017年JavaScript测试概览

Herylee · 2017-05-30翻译 · 409阅读 原文链接

这个简短的指南旨在让您了解JavaScript测试的最重要的推论,术语,工具和方法。它结合了许多关于这里讨论的一些方面的最新文章,并从我们的经验中增加了一些。

看看下面的Jest的Logo,一个Facebook的测试框架。

你可以看到,他们的口号承诺了一个“无痛”的JavaScript测试,但是“有人从评论中说”:

事实上,Facebook有一个很好的理由使用这个口号。一般来说,JS开发人员对网站测试并不满意。JS测试往往是有限的,难以实现,而且很缓慢。

然而,通过正确的策略和恰当的工具组合,可以实现几乎全面的覆盖,测试可以非常有条理,简单,而且相对较快。

值得一提的是,在撰写本博客文章时,我遇到了许多极好的没有维护的库,如果公司决定复活和维护它们,其独特的功能可能在各种情况下可能非常有用,例如DalekJS。

我希望在未来的博客文章中总结一下这些内容,但现在,让我们来关注趋势维护的库。

测试类型

您可以在这里以及深度阅读有关不同测试类型的信息。一般来说,最重要的测试类型是:

  • 单元测试 - 通过mocking输入并确保输出符合预期,测试各个功能或类。

  • 集成测试 - 测试几个模块,以确保它们如期望一样一起工作。

  • 功能测试 - 无论内部结构如何,都可以在产品本身(例如浏览器)上测试一个场景,以确保预期的行为。

测试工具类型

测试工具可以分为以下功能。有些为我们提供了一个功能,有些可以为我们提供了一个组合功能。

通常使用一套工具,即使在理论上可以完成相同的功能,以获取最适合的功能满足您的需求。

  1. 提供测试环境(Mocha,Jasmine,Jest,Karma)

  2. 提供测试结构(Mocha,Jasmine,Jest,Cucumber)

  3. 提供断言功能(Chai,Jasmine,Jest,Unexpected)

  4. 生成,display和watch**测试结果(Mocha,Jasmine,Jest,Karma)

  5. 生成和比较组件和数据结构的快照,以确保以前运行的更改(Jest,Ava)

  6. 提供mocks,spies和stubs(Sinon,Jasmine,enzyme,Jest,testdouble)

  7. 生成代码覆盖率报告(Istanbul,Jest)

  8. 提供一个浏览器或类似浏览器的环境,控制他们的场景执行(Protractor,Nightwatch,Phantom,Casper)

我们来解释上面提到的一些术语:

测试结构是指您的测试的组织。测试通常以支持BDD(行为驱动开发)的BDD结构组织,通常如下所示:

describe('calculator', **function**() {

 // describes a module with nested "describe" functions
  describe('add', **function**() {

 // specify the expected behavior
    it('should add 2 numbers', **function**() {

 //Use assertion functions to test the expected behavior    
    })

 })

})

断言功能是确保测试按照预期的方式产生的功能,其中最受欢迎的是前两个:

// Chai expect
expect(foo).to.be.a('string')
expect(foo).to.equal('bar')

// Jasmine expect
expect(foo).toBeString()
expect(foo).toEqual('bar')

// Chai assert
assert.typeOf(foo, 'string')
assert.equal(foo, 'bar')

// Unexpected expect
expect(foo, 'to be a', 'string')
expect(foo, 'to be', 'bar**'**)

提示:这是一篇关于高级Jasmine断言的好文章。

Spies提供有关应用程序中使用或为测试创建的功能信息 - 调用多少次,在什么情况下,以及由谁?它们在集成测试中特别有用,我们希望在运行内部方案时确保某些行为。例如,在某些处理过程中调用多少次计算函数?

it('should call method once with the argument 3', () => {
  **const** spy = sinon.spy(object, 'method')

 spy.withArgs(3)

 object.method(3)

 assert(spy.withArgs(3).calledOnce)
})

Stubbingor dubbing(如电影中的双打)会用我们自己的功能替换所选功能,以确保所选模块按照预期的行为来实现。

如果我们想确保user.isValid()在测试期间总是返回true,例如,你可以这样做:

sinon.stub(user, 'isValid').returns(**true**) _// Sinon_

spyOn(user, 'isValid').andReturns(**true**) _// Jasmine_

这也符合promises:

it('resolves with the right name', done => {

 **const** stub **=** sinon.stub(User.prototype, 'fetch')
    .resolves({ name: 'David' })

  User.get()
    .then(user => {
      expect(user.name).toBe('David')
      done()
    })

})

Mocks 或 Fakes是伪造某些模块或行为,以确保使用已知输入进行测试。例如,Sinon可以伪造服务器来确保快速和预期的结果。

it('returns an object containing all users', done => {

 **const** server **=** sinon.fakeServer.create()

 server.respondWith('GET', '/users', [
    200,
    { 'Content-Type': 'application/json' },
    '[{ "id": 1, "name": "Gwen" },  { "id": 2, "name": "John" }]'
  ])

 Users.all()
    .done(collection => {
      **const** expectedCollection = [
        { id: 1, name: 'Gwen' },
        { id: 2, name: 'John' }
      ]

 expect(collection.toJSON()).to.eql(expectedCollection)

 done()
    })

  server.respond()
  server.restore()
});

Snapshot Testing是当您将结果数据结构与预期结果进行比较。例如,以下Jest测试模拟“Link”组件的呈现,然后将其保存为JSON形式。

然后将其与先前运行的结果进行比较。如果有任何改变,开发者被提示同意改变是有意义的。

it('renders correctly', () => {
  const linkInstance = (
    <Link page="http://www.facebook.com">Facebook</Link>
  )

  const tree = renderer.create(linkInstance).toJSON()

  expect(tree).toMatchSnapshot()
})

将它们放在一起

如果可能,我们建议对所有测试类型使用相同的工具。针对这些,运用相同的测试结构和语法(2),断言函数(3),结果报告和观察(4)。有时甚至可以使用相同的测试环境(1)来进行部分或全部测试。

确保只能按需运行特定类型的测试。

  • 对于单元测试,为所有单元提供mocked输入(6),并确保其输出是预期的(3),还要确保使用覆盖报告工具(7)覆盖哪些单元。

  • 对于集成测试,定义重要的跨模块内部场景。与单元测试相比,您将使用spies和stubs来测试预期的行为,而不是仅仅声明输出(6)。此外,浏览器或类似浏览器的环境可以测试集成进程之间的及其在UI上的结果。

  • 对于功能测试,将使用具有可编程API(8)的浏览器或类似浏览器的环境来创建类似用户的行为场景。

一些主要测试工具的清单

[node-jsdom从原始jsdom分支3.x克隆实现DOM和HTML标准的JavaScriptwww.npmjs.com](https://www.npmjs.com/package/node-jsdomhttps://www.npmjs.com/package/node-jsdom“)[](https://www.npmjs.com/package/node-jsdom

JSDom是WHATWG DOM和HTML标准的JavaScript实现。换言之,JSDom模拟浏览器的环境,而不需要运行任何东西,仅仅是纯JS。

在这个模拟的浏览器环境中,测试可以运行得很快。JSDom的缺点是,并不是所有的东西都可以在真正的浏览器之外模拟(你不能以截图为例),所以使用它将限制你的测试范围。

值得一提的是JS社区迅速改善。

[ istanbul-js:用JS编写的JavaScript代码覆盖工具_为Javascript提供另一个代码覆盖工具_gotwarlost.github.io](https://gotwarlost.github.io/istanbul/https://gotwarlost.github.io/istanbul/") [] (https://gotwarlost.github.io/istanbul/)

Istanbul将告诉您,您的代码中有多少是单元测试。它将以百分比形式报告声明,线,功能和分支覆盖率,以便您更好地了解剩下的内容。

[PhantomJS_PhantoJs是一款使用JavaScript API编写的无WebKit脚本。它具有快速和本地支持各种Web标准:DOM处理,CSS选择器,JSON,Canvas和SVG._phantomjs.org](http://phantomjs.org/http://phantomjs.org/”)[] (http://phantomjs.org/

它具有快速和本地支持各种Web标准:DOM处理,CSS选择器,JSON,Canvas和SVG._phantomjs.org](http://phantomjs.org/http://phantomjs.org/”)[] (http://phantomjs.org/

在撰写本文时,它非常受欢迎,但由于Google将本机Google Chrome浏览器中运行“无头”功能添加到其中,因此不再保留

[karma-runner/karma_karma - 用于JavaScript 的优秀测试运动器_github.com](https://github.com/karma-runner/karmahttps://github.com/karma-runner/karma") /karma-runner/karma)

Karma允许您在浏览器中运行测试,包括真正的浏览器,Phantom,jsdom甚至是旧版浏览器。

Karma拥有一个带有特殊网页的测试服务器,可以在页面的环境中运行测试。这个页面可以运行在许多浏览器上。

这也意味着测试可以使用像BrowserStack这样的服务远程运行。

[Chaijs/chai_chai - 用于node.js的BDD / TDD assertion框架和可与任何测试框架配对的浏览器._github.com](https://github.com/chaijs/chaihttps://github.com/chaijs/chai“)[](https://github.com/chaijs/chai

Chai是最受欢迎的断言库。

[unexpectedjs/unexpected_unexpected - Unexpected - 可扩展的BDD断言套件_github.com](https://github.com/unexpectedjs/unexpectedhttps://github.com/unexpectedjs/unexpected”)[https://github.com/unexpectedjs/unexpected)

Unexpected是一个与chai语法略有不同断言库。它也是可扩展的,所以断言是基于它的更高级的库,如unexpected-react,你可以在这里更深入地阅读。

[ Sinon.JS - 用于JavaScript的独立测试spies,stubs和mocks。适用于任何单元测试框架。_单独地针对JavaScript测试的spies,stubs和 mocks。适用于任何单元测试framework._sinonjs.org](http://sinonjs.org/http://sinonjs.org/")[html (http://sinonjs.org/

Sinon是一个非常强大的独立测试spies,stubs和mocks 的JavaScript,可以与任何单元测试框架一起使用。

[testdouble.js_testdouble.js - 使用JavaScript针对TDD的最小测试双库_github.com](https://github.com/testdouble/testdouble.jshttps://github.com/testdouble/testdouble.js")[] ://github.com/testdouble/testdouble.js)

testdouble是一个类似于Sinon的新库,在设计,哲学和功能上有一些差异,可以使它在许多情况下有用。你可以在这里,这里和这里阅读。

[Wallaby - 用于JavaScript的集成连续测试运行器_有效和快速集成的连续测试运行器,用于JavaScript,TypeScript,CoffeeScript。它允许连续... _wallabyjs.com](https://wallabyjs.com/https://wallabyjs.com/")

Wallaby是另外一个值得一提的工具。它不是免费的,但许多用户建议购买它。它运行在您的IDE(它一次支持所有主要的),并运行与您相关的代码更改测试,并说明如果有任何失败可以实时追溯你的代码。

选择您的框架

你可能做出的第一个选择是你想要使用的框架和支持的库。建议使用您的框架所提供的工具,直到需要独特的工具出现。那么应该很容易改变或添加它们。

  • 简而言之,如果你想“开始”或寻找一个大型项目的快速框架,请选择Jest。
  • 如果您想要一个非常灵活和可扩展的配置,请去Mocha。如果你想要简单,请使用Ava。
  • 如果你想要真正的低级别,请用Tape。

以下列出了其中一些优缺点的最突出的工具:

[jasmine/jasmine_简单的 JavaScript测试框架为浏览器和node.js_github.com](https://github.com/jasmine/jasminehttps://github.com/jasmine/jasmine")jasmine/jasmine)

Jasmin是一个测试框架,提供您所期望测试所需的一切:运行环境,框架,报告,断言和mocking工具。

  • Globals-默认创建测试全局变量,因此不需要它们:

    // "describe" is in the global scope already // so no these require lines are not required: // // const jasmine = require('jasmine') // const describe = jasmine.describe

    describe('calculator', function() { ... })

  • Ready-To-Go - 与assertions,spies,mocks相当于像Sinon一样的库。如果您需要一些独特的功能,库仍可轻松使用。

  • Angular - 有广泛的Angular支持

[mochajs /mocha_简单,灵活,有趣的JavaScript测试框架为node.js&browser_github.com](https://github.com/mochajs/mochahttps://github.com/mochajs/mocha")[](https:/ /github.com/mochajs/mocha)

Mocha是目前使用最多的库。与Jasmine不同的是,它使用第三方断言,mocking和spies工具(通常是Enzyme 和 Chai)一起使用。

这意味着mocha更难建立并分成更多的库,但更加灵活和扩展。

例如,如果你想要特殊的断言逻辑,你可以fork Chai,并用你自己的断言库替换Chai。这也可以在asmine中完成,但是在某种意义上,Mocka更灵活。

  • 社区 - 有许多插件和扩展来测试独特场景。

  • 可扩展性 - 插件,扩展和库,如Sinon包括Jasmine没有的功能。

  • 全局 - 默认创建测试结构全局变量,但显然不是断言,spies和mocks,如Jasmine - 有些人对这种看起来不一致的全局变量感到惊讶。

[这是无痛的JavaScript测试🃏facebook.github.io](https://facebook.github.io/jest/https://facebook.github.io/jest/")[https://facebook.github.io/jest/

Jest是Facebook推荐的测试框架。它涵盖了Jasmine,并在其上添加了的功能,所以所有提及关于Jasmine的也适用于它。

_阅读不人性化的文章和博客文章后,到2016年底,令人难以置信的是Jest的速度和便利性让人印象深刻。

  • 性能 - 首先,通过实施aclever并行测试机制(例如我们从我们的经验和在这些博客文章中:在这里,这里,这里),大多数测试文件的大项目被认为是更快的。

  • UI-清晰方便。

  • 快照测试 - 快照由Facebook开发和维护,尽管它可以在几乎任何其他框架中使用,作为框架集成工具或使用正确的插件的一部分。

  • 改进的模块嘲笑 - Jest允许您以非常简单的方式模拟重型库,以提高测试速度。

  • 代码覆盖 - 包括基于伊斯坦布尔的强大而快速的内置代码覆盖工具。

  • 支持 - 截至2016年底和2017年的头几个月,Jest正在进行大步骤的改善。

  • 开发人员只更新更新的文件,因此测试在监视模式下运行非常快。

[avajs/ava_未来 JavaScript 运行器_github.com](https://github.com/avajs/avahttps://github.com/avajs/ava")[https://github.com/avajs/ava

Ava是一个简单的测试库,并行运行测试。

  • Globals-不会创建任何测试全局变量,因此您可以更好地控制测试。

  • 简单 - 简单的结构和断言,无需复杂的API,同时支持许多高级功能。

  • 开发 - Ava仅更新更新的文件,以便测试在观察模式下快速运行。

  • 断点打印测试通过使用jest-snapshot支持。

[substack/tape_tape - tap - 用于节点和浏览器的生成测试工具_github.com](https://github.com/substack/tapehttps://github.com/substack/tape") /substack/tape)

Tape是他们所有中最简单的。它只是一个JS文件,运行节点具有非常短的“to-the-point”API。

  • 简单 - 简单的结构和断言,没有复杂的API。甚至超过Ava。

  • 全局性- 不会创建任何测试全局变量,因此您可以更好地控制测试。

  • 测试之间没有共享状态 - Tape阻止使用诸如“beforeEach”之类的功能,以确保测试模块化和最大用户对测试周期的控制。

  • 不需要CLI - Tape可以在任何JS可以运行地方运行。

单元测试

覆盖一切使用像Istanbul这样的覆盖工具来确保系统中的每个模块都被覆盖。

由于这些测试正在测试单独的模块,因此选择在NodeJS中运行它们,而不是在浏览器中运行(如karma),因为在浏览器中运行JS的速度比在NodeJS中慢。

集成测试

集成测试 - 当在您开发中或后续的开发,创建一个具有空测试的重要内部流程列表作为TODO,并逐个实施这些测试。考虑添加UI mocking和断点打印。

断点打印测试可以很好地代替传统的UI集成测试。不用在某些进程之后测试部分UI,您可以在应用程序中使用断点打印代替。

考虑使用JSDom或Karma在真实的浏览器中运行您的测试。

功能测试

用于此功能的永久性工具的数量是有限的,并且它们的实现彼此之间有很大差异,因此强烈建议在作出决定之前尝试实施其中的一些工具。

  • 简而言之,如果您想要使用最简单的设置“开始”,如果您想要轻松测试许多环境,请使用TestCafe。
  • 如果您想与流程一起使用并具有最大限度的社区支持,如果您需要能够在JS中编写测试,Selenium就是您要走的路。
  • 如果您的应用程序没有复杂的用户交互和图形,例如,如果要测试一个充满表单和导航的系统,无障碍浏览器工具(如Casper)将为您提供最快的测试。

[SeleniumHQ /selenium_selenium - 浏览器自动化框架和生态系统._github.com](https://github.com/SeleniumHQ/seleniumhttps://github.com/SeleniumHQ/selenium")[] (https://github.com/ SeleniumHQ /selenium)

SleniumHQ,更好地称为Selenium,自动化浏览器来模拟用户行为。它不是专门用于测试的,并且可以通过在浏览器上使用API​​模拟用户行为,并利用暴露服务器来达到控制浏览器的目的。

Selenium可以通过多种方式和使用各种编程语言进行控制,甚至可以使用一些工具,不需要编程。

然而,对于我们的需求,Selenium服务器由Selenium WebDriver控制,作为NodeJS和操作浏览器的服务器之间的通信层。

Node.js WebDriver Selenium Server FF / Chrome / IE / Safari

WebDriver可以导入到测试框架中,测试可以作为其中的一部分:

describe('login form', () **=>** {

 before(() **=>** {
    return driver.navigate().to('http://path.to.test.app/')
  })

 it('autocompletes the name field', () => {

 driver.findElement(By.css('.autocomplete'))
      .sendKeys('John')

 driver.wait(until.elementLocated(By.css('.suggestion')))

 driver.findElement(By.css('.suggestion')).click()

 **return** driver.findElement(By.css('.autocomplete'))
      .getAttribute('value')
      .then(inputValue **=>** {
        expect(inputValue).to.equal('John Doe')
      })
  })

  after(() **=>** {
    return driver.quit()
  })

})

WebDriver本身可能对您而言足够,实际上有些人建议使用它,但是通过分割和更改它或通过封装来创建各种库来扩展它。

确实封装WebDriver可能会添加冗余代码,并且可以使调试变得更加困难,并且可能会使其偏离正在进行的开发(就2017年)。

仍然有些人喜欢不直接使用它。我们来看一些关于selenium库的操作:

[angular/protractor_protractor - Angular apps的E2E测试框架_github.com](https://github.com/angular/protractorhttps://github.com/angular/protractor")protractor)

Protractor是一个包装Selenium的库,为Angular添加了一个改进的语法和特殊的内置钩子。

  • Angular - 有特殊的钩子,虽然可以成功地与其他JS框架一起使用。

  • 错误报告 - 良好的机制。

  • Mobile - 不支持自动化移动Apps。

  • 支持 - 可以使用TypeScript支持,库由庞大的angular团队操作和维护。

[WebdriverIO - 用于NodeJS的Selenium 2.0绑定_Selenium 2.0 / webdriver._webdriver.io的nodejs绑定实现](http://webdriver.io/http://webdriver.io/")

WebdriverIO自己实现了selenium WebDriver。

  • 语法 - 非常简单易读。

  • 灵活 - 非常简单和不可知,甚至用于测试,灵活和可扩展的库。

  • 社区 - 它具有开发人员社区良好的和热情的支持,使其能够与插件和扩展。

[Nightwatch.js_在针对Selenium / WebDriver server._nightwatchjs.org]运行的Node.js中,提供高效,直接的端到端测试(http://nightwatchjs.org/http://nightwatchjs.org/”)[]( http://nightwatchjs.org/

Nightwatch有自己的selenium WebDriver的实现。并提供自己的测试框架与测试服务器,断言和工具。

  • 框架 - 也可以与其他框架一起使用,但如果您希望运行功能测试而不是其他框架的一部分,则可能会特别有用。

  • 语法 - 看起来最简单,最可读。

  • 支持 - 没有typescript支持,一般来说,这个库似乎比其他库的支持略少。

[Casperjs / casperjs_casperjs - PhantomJS和SlimerJS_github.com的导航脚本和测试实用程序](https://github.com/casperjs/casperjshttps://github.com/casperjs/casperjs") / casperjs / casperjs)

Casper写在Phantom和Slimer之上(与Phantom相同,但在FireFox的Gecko中),以便在创建Phantom和Slimer脚本时提供导航,脚本和测试实用程序,并抽取许多复杂异步的东西。

Casper和其他无头浏览器为我们提供了一种更快速但不太稳定的方式,在无UI浏览器上运行功能测试。

[现代Web开发栈的自动浏览器测试| TestCafe_TestCafe是一个针对测试网站和应用程序的简单而强大的框架。它允许您轻松创建,维护和... _devexpress.github.io](https://devexpress.github.io/testcafe/https://devexpress.github.io/testcafe/")[](https:/ /devexpress.github.io/testcafe/)

TestCafe是基于Selenium的工具的绝佳选择。

在2016年10月,它发布了作为开源JavaScript框架的TestCafe的核心库。仍然有一个[付费版本](https://testcafe.devexpress.com/)提供非JS工具,如测试记录器和客户支持。

这很重要,因为许多过时的文章表示它的代码是关闭的,并且将其置于不利地位。

它作为JS脚本注入浏览器的环境,而不是使用像selenium这样的插件附加到浏览器。这允许TestCafe在任何浏览器(包括移动设备)上运行。在Selenium上,您必须为每个设备和浏览器安装特殊插件。

TestCafe是更新和更多的JS和测试导向。它具有非常有用的错误报告系统,指示故障的一行,非常有用的选择器系统以及一些更有用的功能。

[cucumber/cucumber-JS_cucumber-js - Cucumber for JavaScript_github.com](https://github.com/cucumber/cucumber-jshttps://github.com/cucumber/cucumber-js")[] (https://github.com/cucumber/cucumber-js)

Cucumber是另一个有效的功能测试框架。这是一个以稍微不同的方式管理前面提到的自动化测试的框架。

Cucumbe帮助在BDD中进行书面测试,在使用Gherkin语法编写验收标准的业务团队和根据它们编写测试的程序员之间进行划分。测试可以用框架支持的各种语言编写,包括我们关注的JS:

features / like-article.feature:(小黄皮语法)

Feature: A reader can share an article to social networks
  As a reader
  I want to share articles
  So that I can notify my friends about an article I liked

  Scenario: An article was opened
    Given I'm inside and article
    When I share the article
    Then the article should change to a "shared" state

features/ stepdefinitions /like-article.steps.js

module.exports = **function**() {
  **this**.Given(/^I'm inside and article$/, **function**(callback) {
    // functional testing tool code
  })

 **this**.When(/^I share the article$/, **function**(callback) {
    // functional testing tool code
  })

  **this**.Then(/^the article should change to a "shared" state$/, **function**(callback) {     
    // functional testing tool code
  }) 
}

如果您认为这种安排可以帮助您公司的不同的员工进行协作,那么您可以发现这个工具帮助很大。

贡献

如果您有任何要我在这个简短的指南中添加或更改内容,请告诉我。我会很乐意去做。我希望本指南尽可能完整和有用。

结论

在这个非常简短的指南中,我们看到了JS社区中最热门的测试方案和工具,希望您能够更轻松地测试应用程序。

请记住,还有很多有用的工具和策略本指南没有涵盖到。有些与这里涵盖的工具有关,有些与之完全不同。

最后,通过理解由非常活跃的社区开发的一般解决方案模式,并结合您自己的经验和对应用程序的特性的理解,并且它的特殊需求,可以制定当今应用架构的最佳决策。

哦,写作,重写,重写,重写,测试不同的解决方案:)

祝您快乐地测试:)

谢谢 :)

相关文章