关于单元测试
  前端的单元测试也可以称为自动化测试,测试驱动开发,单元测试对于前端模块化、框架和功能库的开发是非常有必要的,只要做好模块的解耦和功能划分,单元测试可以愉快地进行。好的单元测试(全面的功能、抛错和边缘覆盖)可以成为项目开发或修改完成后是否能“安全上线”的重要判断依据之一。
  引进跨平台测试
  开发和运行单元测试通常是在开发人员的电脑上完成,而任何一台电脑上所能安装的浏览器是远远不能满足测试兼容性需求的,如果需要配备各种平台的电脑也是非常浪费人力物力。那么有没有工具能自动把本地的测试代码在所有平台上都跑一遍,并且追踪完整的测试过程,把 log 信息反馈回本地?有!那是云测试工具。
  目前国内外的云测试工具和方案有很多,因为云测试的实质是运行远程虚拟机,需要大量的服务器和齐全的设备,所以大多数都是收费的。这里推荐 SauceLabs,它虽然也是收费的,但是新注册的账号可以提供 8 个并发测试,免费 90 个小时的自动测试时长,而且操作简单,用户体验非常友好,很多的前端框架比如 Vue 等也在使用它进行兼容性测试。
  这篇文章详细介绍一下如何利用 SauceLabs 这个云测试工具进行跨平台的 JavaScript 单元测试,让你的代码可以轻松的通过所有版本的 Windows IE, Mac Safari 以及各种移动设备浏览器的测试评估,测试后还可以生成浏览器测试状态矩阵图:

  主流的 JavaScript 测试框架在 SauceLabs 上都有文档和配置说明,其中 Karma 是配置为简单易懂的测试框架之一,下文详细介绍下 Karma + SauceLabs 进行跨平台单元测试的整个流程,以及我自己在使用中遇到的一些问题和解决方法,如果你不用 Karma,可以在 这里 查找其他测试框架的配置方法。
  以下步骤的前提是你已经写好了测试用例,如果你还不知道如何编写和组织单元测试,可以参考 js-test-workflows 的简单例子来开始学习 JavaScript 单元测试。
  配置步骤
  1. 安装 Karma
  整个 SauceLabs 测试都是基于 Karma 配置文件 karma.sauce.js 完成的,通过插件 karma-sauce-launcher 自动完成远程服务器的连接和测试代码的提交。如果你的项目已经配备好了 Karma 和断言库,可以忽略此步骤。
  安装测试框架 Karma
  npm install karma -g
  安装测试断言库 jasmine (也可以选择其他库如 assert, should 等)
  npm install karma-jasmine
  2. 安装 karma-sauce-launcher
  这个工具是让 Karma 和 SauceLabs 建立连接的桥梁,它的作用是把本地的测试代码提交到云端测试虚拟机上,告诉 SauceLabs 要启动哪个系统哪个版本的浏览器来跑我们的测试代码,并且把测试结果、错误信息和追踪栈返回到本地的终端,一旦测试不通过,可以根据返回的信息来调试出错的代码。
  npm install karma-sauce-launcher --save-dev
  3. 注册 SauceLabs 并获取 accessKey
  SauceLabs 免费注册地址:https://saucelabs.com/signup/...
  注册完成后,登陆账号,进入 Dashboard, 点击右下角的用户名,弹出菜单选择 User Setting 进入用户设置:
  找到 USERNAME 和 Access Key:

  在项目目录下建立一个 sauce.json 文件来记录上面的 userName 和 accessKey,这两个字段的作用是连接 SauceLabs 服务器的身份验证。
  {
  "username": "xxxx",
  "accesskey": "xxx"
  }
  4. 编写测试配置文件
  在项目目录下添加测试配置文件 karma.sauce.js 该文件是整个跨平台测试的主要文件,也是 Karma 的入口文件,配置内容如下:
var sauce = require('./sauce.json'); // 引进 userName 和 key
// 生成一个 SauceLabs 浏览器配置信息,可以指定运行的系统和浏览器版本
function createCustomLauncher (browser, platform, version) {
return {
base: 'SauceLabs',
browserName: browser,
platform: platform,
version: version
};
}
// 定义所有需要在云端测试的平台和浏览器
// 名字的定义是随意的,SauceLabs 只会根据配置内容来启动对应的浏览器
// 所有完整的平台设备列表:https://saucelabs.com/platforms
var customLaunchers = {
// 主流浏览器
sl_win_chrome: createCustomLauncher('chrome', 'Windows 7'),
sl_mac_chrome: createCustomLauncher('chrome', 'OS X 10.10'),
sl_win_firefox: createCustomLauncher('firefox', 'Windows 7'),
sl_mac_firefox: createCustomLauncher('firefox', 'OS X 10.10'),
sl_mac_safari: createCustomLauncher('safari', 'OS X 10.11'),
// 移动设备浏览器
sl_ios_8_safari: createCustomLauncher('iphone', null, '8.4'),
sl_ios_9_safari: createCustomLauncher('iphone', null, '9.3'),
sl_android_4_2: createCustomLauncher('android', null, '4.2'),
sl_android_5_1: createCustomLauncher('android', null, '5.1'),
// Microsoft Edge
sl_edge: createCustomLauncher('MicrosoftEdge', 'Windows 10'),
// IE 浏览器
sl_ie_9: createCustomLauncher('internet explorer', 'Windows 7', '9'),
sl_ie_10: createCustomLauncher('internet explorer', 'Windows 8', '10'),
sl_ie_11: createCustomLauncher('internet explorer', 'Windows 10', '11')
};
// Karma 配置参数
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (KarmaConfig) {
// 将 SauceLabs 提供的 username 和 accesskey 放到环境变量中
if (!process.env.SAUCE_USERNAME || !process.env.SAUCE_ACCESS_KEY) {
process.env.SAUCE_USERNAME = sauce.username;
process.env.SAUCE_ACCESS_KEY = sauce.accesskey;
}
// 设置测试的超时时间
var maxExecuteTime = 5*60*1000;
KarmaConfig.set({
// 加载测试文件的根目录
basePath: '',
// 使用的断言库
frameworks: ['jasmine'],
// 告诉 Karma 要将哪些 js 文件加载到浏览器运行测试
files: [
'../src/**/*.js',
'../test/**/*.js'
],
// SauceLabs 的配置,这里只需要配置几个重要的字段即可,完整的字段可以参考:
// https://wiki.saucelabs.com/display/DOCS/Test+Configuration+Options
sauceLabs: {
// 测试结果是否公开,如果希望生成矩阵图,必须是 public
public: 'public',
// 是否在测试过程记录虚拟机的运行录像
recordVideo: false,
// 是否在测试过程记录虚拟机的图像
recordScreenshots: false,
// 测试名称
testName: 'Cross browsers test',
// 测试的记录号,可以为任意字符,如果希望生成矩阵图,build 不能为空
build: 'build-' + Date.now()
},
// 自定义运行测试的 SauceLabs 浏览器
customLaunchers: customLaunchers,
browsers: Object.keys(customLaunchers),
// 测试处理的报告程序
reporters: ['progress', 'saucelabs'],
// 大超时时间
captureTimeout: maxExecuteTime,
browserNoActivityTimeout: maxExecuteTime
});
}