[译] 进修怎样构建自动化、跨浏览器的 JavaScript 单元测试

作者:Philip Walton
译者:Yeaseon
原文链接:Learning How to Set Up Automated, Cross-browser JavaScript Unit Testing

译文仅供个人进修,不用于任何情势贸易目标,转载请说明原作者、文章泉源、翻译作者及链接,版权归原文作者一切。

我们都晓得在多个浏览器中测试我们的代码是何等的主要。最少在我们宣布第一个项目标时刻,我以为我们在收集开辟社区做大部份事变照样相称不错的。

我们做的不够好的事变是测试代码时每一次做出的转变。

我个人对此感到很忸捏。我已把“进修怎样构建自动化、跨浏览器的JavaScript的单元测试”列在我的年度to-do清单中,但我每一次坐下来真正想要做的时刻,我又猬缩了。虽然我一定这一部份缘由是由于我的懒散,同时我以为这也是由于缺乏优越的可用信息在这个主题上。

有许多东西和框架(比方 Karma)宣称“要使自动化的JavaScript测试变得简朴”,但以我的履历看来这些东西引入的庞杂性比他们挣脱的庞杂性更多。在我的事变履历中,如果你是一个专家这些东西“能事变”的很好,但关于一个初学者是很蹩脚的。我想要真正相识的是这个流程是怎样在引擎中事变的,以便在它出现题目的时刻(总会出现题目的),我能处理它。

对我来讲,充足相识这些是怎样事变的最好要领就是尝试从头最先从新竖立它。所以我决议去构建我本身的测试东西,然后把我的所学分享到社区中。

手工测试流程

在我诠释自动化历程之前,我以为最主要的是确保我们都在统一页面上举行手工测试事变。

毕竟,自动化是关于运用机械来封闭负载的重复部份的现有事变流程。如果你在充足明白手工历程之前尝试去最先自动化,它也不会像你明白了自动化历程一样。

在手工历程当中,你写了一个你的测试文件,它能够看起来像是:

var assert = require('assert');
var SomeClass = require('../lib/some-class');

describe('SomeClass', function() {
  describe('someMethod', function() {
    it('accept thing A and transforms it into thing B',function() {
      var sc = new SomeClass();
      assert.equal(sc.someMethod('A'), 'B');
    });
  });
});

这个例子用了Mocha和Node.js 资本模块,然则主要的不是你是用的测试库或许断言库,它能够使恣意一个。

在Mocha中运转Node.js,在你终端经由过程敕令行你就能够运转这个测试:

mocha test/some-class-test.js

你须要一个带有<script>标签的HTML文件加载这段剧本,才能在浏览器运转这个测试,浏览器并不熟悉require声明,你须要一个像是browserify或许webpack的模块打包东西去处理这些依靠。

browserify test/*-test.js > test/index.js

像是browserify或是webpack的模块打包东西的优点就是它能整合你的一切测试(也包含依靠)到一个单一的文件中,如许就能够很轻易加载到你的测试页面。

一个用Mocha写的典范测试文件看起来像是如许的:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Tests<title>
  <link href="../node_modules/mocha/mocha.css" rel="stylesheet" />
  <script src="../node_modules/mocha/mocha.js"></script>
</head>
<body>

  <!-- A container element for the visual Mocha results -->
  <div id="mocha"></div>

  <!-- Mocha setup and initiation code -->
  <script>
    mocha.setup('bdd');
    window.onload = function() {
      mocha.run();
    };
  </script>
  <!-- The script under test -->
  <script src="index.js></script>
</body>
</html>

如果你不运用Node.js,那末你的出发点看起来已很像这个HTML文本了,唯一差别的是你的依靠能够须要列成一个个零丁的<script>标签。

毛病检测

如果一个测试由于是断言不正确,你的断言库任何时候都邑抛出一个毛病,这个时刻你的测试框架就能够发明这个毛病。测试框架运转在每一个测试的try/catch中来捕捉能够会抛出的毛病,这些毛病报告会显如今你的页面中或是在console中显现这些log。

大多数的测试框架(像是Mocha)将会供应钩子,如许你就能够在测试历程当中让页面中的其他剧本接见测试效果。这是一个自动化测试历程的一个症结特性,由于为了自动化事变,自动化剧本须要能够提取测试剧本的效果。

手工体式格局的优点

在浏览器中手工运转测试的最大优点是,如果你的一个测试失利了你能用浏览器的开辟者东西去调试它。

像是如许的一个简朴例子:

describe('SomeClass', () => {
  describe('someMethod', () => {
    it('accepts thing A and transforms it into thing B', () => {
      const sc = new SomeClass();

      debugger:
        assert.equal(sc.someMethod('A'), 'B');
    )};
  )};
)};

如今当你从新打包并革新浏览器翻开开辟者东西,你就能够够经由过程你的代码,很轻易定位到题目的泉源地点。

相比之下,大多数盛行的自动化测试框架使这变得很难题!它们供应的轻易的地方是它们绑缚了你的单元测试而且为你竖立一个宿主的HTML页面。

在你的任何一个测试都不会失利的时刻,这是很好的体式格局。由于当它们如许做时,就没有办法轻松地reproduce和当地调试。

自动化流程

手工流程有它的有点,同时也有一些瑕玷。翻开几个浏览器去运转测试,每次你想做出修改的时刻都邑变得烦琐且轻易失足。更不用说,我们大部份人没有装置每一个浏览器的每一个版本到我们的当地开辟机械上。

如果你在仔细的测试你的代码,并愿望确保它的每一个变化都做恰当的,那末你须要自动化这个流程。

不管你是何等的自发,手动测试是太轻易遗忘或疏忽,终究它不会充足利用你的时候。

然则自动化测试一样也有它的不足。过于频仍的自动测试东西引入了一个全新的题目。细微差别的构建,测试就会变得差别,测试失利的话面对的将是痛楚的调试。

当我设想怎样构建我的自动化测试体系的时刻,我不想再掉进这个圈套和落空手工测试流程的便利性。所以我决议在最先之前做一个需求列表。

毕竟,一个自动化体系如果引入了新的使人头疼的贫苦和庞杂性,那它就不是一个胜利的自动化体系。

需求

  • 我须要能够运用敕令行运转测试

  • 我须要能够在当地调试失利测试

  • 我须要一切必需的依靠经由过程npm运转测试就能够被装置,所以任何人检察我的代码就能够很简朴的运转,经由过程

    npm install && npm test
  • 我须要运转在CI机械上的测试流程和运转在我的开辟机械一样简朴。如许构建体式格局是雷同的,而且无需搜检新的变化就能够调试毛病。

  • 我须要一切的测试我(或许恣意人)提交新的变化或许拉取要求都能在任何时候自动化运转。

有了这个大略的列表以后,下一步就是深切到在主流的云测试怎样自动化,跨浏览器测试的事变。

云测试怎样事变

有许多云测试的供应商,每一个供应商都有本身的优点和弊端。我是一个开源作者,所以我只看那些供应开源项目标供应商,它们当中,只需Sauce Labs是唯一一个不须要我邮箱支撑就能够启动一个新的开源账户。

更令我受惊的是当我真正最先研讨Sauce Labs关于JavaScript单元测试的文档是有何等简朴。由于许多测试框架都有宣称让单元测试变得简朴,我以为这真的很难!

我前面强调了一点就是,我不想我的自动化流程和我的手工流程有什么根本上的差别。事实证明,Sauce Labs供应的自动化要领真的很像我的手工要领。

这里是所触及的步骤:

  1. 你给Sauce Labs一个你测试页面的URL以及你要运转的测试的浏览器/平台列表。

  2. Sauce Labs运用selenium webdriver去加载你给它的每一个浏览器和平台的组合的测试页面。

  3. WebDriver搜检网页是不是测试失利,并将效果存储。

  4. Sauce Labs将有用的效果给你。

这真的很简朴。

我毛病地假定你不得不把你的JavaScript代码给Sauce Labs,而且它将会运转在它的机械上,而不是它们只是去接见你给它们的URL。如许的话看起来就像手工流程了;唯一差别的是Sauce Labs去翻开一切的浏览器并为你记录下效果。

API要领

Sauce Labs有两个运转单元测试的API要领:

StartJS Unit Tests要领在你指定的浏览器/平台启动一个测试页面。

文档给了一个运用curl的例子:

curl https://saucelabs.com/rest/v1/SAUCE_USERNAME/js-tests \ 
   -X POST \ 
   -u SAUCE_USERNAME:SAUCE_ACCESS_KEY \ 
   -H 'Content-Type: application/json' \
   --data '{"url": "https://example.com/tests.html", "framework": "mocha", "platforms": [["Windows 7", "firefox", "27"], ["Linux", "chrome", "latest"]]}'

由于这是JavaScript单元测试,我将给你一个运用node模块request的例子,如果你正在用Node.js它能够更靠近你终究要做的:

request({
  url: `https://saucelabs.com/rest/v1/${username}/js-tests`, 
  method: 'POST', 
  auth: { 
    username: process.env.SAUCE_USERNAME, 
    password: process.env.SAUCE_ACCESS_KEY 
  }, 
  json: true, 
  body: { 
    url: 'https://example.com/tests.html', 
    framework: 'mocha', 
    platforms: [ 
      ['Windows 7', 'firefox', '27'], 
      ['Linux', 'chrome', 'latest'] 
    ] 
  }
}, (err, response) => { 
  if (err) { 
    console.error(err); 
  } else { 
    console.log(response.body); 
  }
});

你注意到body中的framework: 'mocha'。Sauce Labs供应商支撑许多主流的JavaScript单元测试框架,包含 Mocha,Jasmine,Qunit和YUI。“支撑”意味着Sauce Labs的webdriver客户端晓得去哪猎取测试效果。

如果你没有运用上面提到的测试框架,你能能够经由过程设置framework: 'custom',Sauce Labs将会替代找到的全局变量window.global_test_results。花样化的效果被列在文档中的自定义框架一节中。

让Mocha测试效果关于Sauce Labs的webdriver客户端有用

只管你在最初的要求中通知Sauce Labs你在运用Mocha,你依然须要去更新你的HTML页面,去存储Sauce Labs能接见的全局变量的测试效果。

为你的HTML页面增添Mocha支撑:

<script>
  mocha.setup('bdd');
  window.onload = function() {
    mocha.run();
  };
</script>

做一些事变,像下面如许:

<script>
  mocha.setup('bdd');
  window.onload = function() { 
    var runner = mocha.run(); 
    var failedTests = []; 

    runner.on('end', function() { 
      window.mochaResults = runner.stats;
      window.mochaResults.reports = failedTests; 
    }); 
    runner.on('fail', logFailure); 
    
    function logFailure(test, err){ 
      var flattenTitles = function(test){ 
        var titles = []; 
        while (test.parent.title){         
          titles.push(test.parent.title); 
          test = test.parent; 
        } 
        return titles.reverse(); 
      }; 
    
      failedTests.push({ 
        name: test.title, 
        result: false, 
        message: err.message, 
        stack: err.stack, 
        titles: flattenTitles(test) 
      }); 
    };
  };
</script>

在上面的代码和默许的Mocha模板中唯一差别的是分配给测试效果的变量名,就像Sauce Labs希冀的花样一样叫做window.mochaResults。由于这个新的代码不会影响正在浏览器中运转的手工测试,你无妨就最先运用它作为默许的Mocha模板。

再次强调一点,当Sauce Labs“运转”你的测试时,它并没有做任何事,它只是纯真的接见一个页面,比及发明一个window.mochaResults对象,然后记录下这个效果。

肯定你的测试经由过程照样失利

StartJS Unit Tests 要领会通知Sauce Labs去挨个在你指定的浏览器/平台运转测试,然则它不会返回测试的效果。

它返回一切事变行列中的ID,相应看起来像是如许的:

{ 
  "js tests": [ 
    "9b6a2d7e6c8d4fd2afeeb0ff7e54e694", 
    "d38688ec7256497da6966f4523ddee76", 
    "14054e68ccd344c0bed77a798a9ce1e8", 
    "dbc54181f7d947458f52201ea5fcb901" 
  ]
}

要肯定你测试经由过程照样失利,你要挪用GetJS Unit Status要领,它接办一个事变行列而且返回当前每一个事变的事变状况。

这个主意是你要按期挪用这个要领,晓得一切事变都完成。

request({ 
  url: `https://saucelabs.com/rest/v1/${username}/js-tests/status`, 
  method: 'POST', 
  auth: { 
    username: process.env.SAUCE_USERNAME, 
    password: process.env.SAUCE_ACCESS_KEY 
  }, 
  json: true, 
  body: jsTests, // The response.body from the first API call.
}, (err, response) => { 
  if (err) { 
    console.error(err); 
  } else { 
    console.log(response.body); 
  }
});

相应的效果看起来像是如许:

{ 
  "completed": false,
   "js tests": [
     { 
      "url": "https://saucelabs.com/jobs/75ac4cadb85e415fae957f7811d778b8", 
      "platform": [ 
        "Windows 10", 
        "chrome", "latest" 
       ], 
       "result": { 
        "passes": 29, 
        "tests": 30, 
        "end": {}, 
        "suites": 7, 
        "reports": [], 
        "start": {}, 
        "duration": 97, 
        "failures": 0, 
        "pending": 1 
      }, 
      "id": "1f74a237d5ba4a47b5a42570ae1e7999", 
      "job_id": "75ac4cadb85e415fae957f7811d778b8" 
    }, 
    // ... the rest of the jobs 
  ]
}

一旦response.body.complete属性值为true,就示意你的测试已运转完成,然后你就能够够经由过程搜检每一个事变流程的经由过程照样失利。

当地接见测试

我已诠释过Sauce Labs“运转”你的测试经由过程接见一个URL。固然,这意味着这个URL必需是公然在收集上可接见的链接。

有一个题目就是如果你的测试效劳启动在localhost

有许多处理这个题目的计划,包含Sauce Connect(官方引荐的一种),这是一个由Sauce Labs竖立的代理效劳器,在Sauce Labs假造机和当地主机之间开启一个平安衔接。

Sauce Labs是处于平安性的斟酌被设想的,而且使得外部没法取得你的代码。它的瑕玷就是非常庞杂的设置与运用。

如果你的代码触及到平安性,它能够值得你去弄清楚Sauce Labs;如果不是的话,有许多类似的计划去更简朴的处理这个题目。

我挑选的计划是ngrok

ngrok

ngrok是一个用于竖立平安隧道衔接东西。它给你一个大众的URL到web效劳器运转在你的当地机械上,确实的是你须要运转测试在Sauce Labs上。

如果你在假造机上举行开辟或手动测试,你能够已听说过ngrok,如果没有,那你应当去查阅一下了,它是极为有用的东西。

在你的机械上装置ngrok像是下载二进制文件,然后增加到你的途径中一样简朴;如果你将会在Node中运用ngrok,你也须要经由过程npm装置它。

npm install ngrok

你能够用下面的代码以编程体式格局从Node中最先ngrok历程:

const ngrok = require('ngrok');

ngrok.connect(port, (err, url) => { 
  if (err) { 
    console.error(err); 
  } else { 
    console.log(`Tests now accessible at: ${url}`); 
  }
});

只需你有一个大众的URL能接见你的测试文件,用Sauce Labs跨浏览器测试你的当地代码会变得非常轻易。

整合碎片化

这篇文章包含了许多主题,给人的印象是自动化的,跨浏览器的JavaScript单元测试是庞杂的。但状况并非如此。

我从我的角度来看这篇文章-当我试图去处理这个题目。然后回忆我之前的履历,真正庞杂的是缺乏处理全部流程怎样事变的有用信息,和怎样把一切的整合到一同。

一旦你相识了一切的步骤,它很简朴。总结:

最初的手工流程

  1. 写一个测试然后竖立一个单一的HTML页面去运转它。

  2. 在当地的一个或许两个浏览器中运转这个测试,确保它能事变。

增添自动化流程

  1. 竖立一个开源的Sauce Labs账号,取得一个用户名和接见权限。

  2. 更新你的测试页面源码,以便Sauce Labs能经由过程JavaScript全局变量读取测试效果。

  3. 用ngrok给你的当地测试页面竖立一个平安隧道,如许就能够在互联网公然的接见了。

  4. 挪用StartJS Unit Tests接口要领列出你想测试的浏览器/平台。

  5. 定时挪用GetJS Unit Test Status要领晓得事变完成。

  6. 报告效果。

使测试变得更轻易

我晓得这篇文章开首我谈了许多关于你不须要一个框架来做自动化,跨浏览器的JavaScript单元测试,我如今依然深信这个。但是,只管每一步都很简朴,你能够不想在每次都为项目编写代码。

我想给我的许多老项目增添自动化测试,所以对我来讲打包这些逻辑到我的模块中是很有意义的。

我引荐你尝试完成一个你本身的框架,如许你就能够够完全明白它是怎样事变的,但如果你没有时候而且还想疾速竖立一个测试,我发起你运用我竖立的库Easy Sauce

Easy Sauce

Easy Sauce是一个Node包和一个敕令行东西,如今我为我想做跨浏览器测试的每一个JavaScript项目都运用这个包。

easy-sauce 敕令能够设置你的HTML测试文件的途径(默许是/test/)、开启当地效劳的端口(默许是1337端口)和一系列的浏览器/平台举行测试。easy-sauce将会在Sauce Lab’s selenium cloud运转你的测试,将日记打印在控制台并经由过程适宜的状况码示知你测试是不是经由过程。

npm包使它变得更轻易,easy-sauce将会默许在package.json文件中查找设置选项,所以你没必要离别的存储它们。优点是用户越发明白的晓得你的包支撑浏览器/平台。

关于easy sauce完全的用法引见,请检察Github文档

末了,我想强调的是我特地竖立这个项目来处理我的须要。虽然我以为这个项目关于许多开辟人员都非常有用,但我没有设想把它变成一个功用完全的测试处理计划。

结语

在这篇文章的最先,我写下了一系列的需求。在Easy Sauce的协助下,我正勤奋的在任何项目中满足这些需求。

如果你还没有为你的项目做自动化、跨浏览器的JavaScript单元测试,我勉励你给Easy Sauce一个尝试的时机。纵然你不想用Easy Sauce,你最少应当相识你本身的需求或更好地相识现有的东西。

Happy testing!

如果你能看到这里,很谢谢你的耐烦浏览。
这是我翻译的第一篇手艺文档,本身程度有限,所以翻译总有不当与疏漏,若有发明还请您耐烦批评指出。

    原文作者:YeaseonZhang
    原文地址: https://segmentfault.com/a/1190000006004525
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞