4

关于单元测试

前端的单元测试也可以称为自动化测试,测试驱动开发,单元测试对于前端模块化、框架和功能库的开发是非常有必要的,只要做好模块的解耦和功能划分,单元测试就可以愉快地进行。好的单元测试(全面的功能、抛错和边缘覆盖)可以成为项目开发或修改完成后是否能“安全上线”的重要判断依据之一。

引进跨平台测试

开发和运行单元测试通常是在开发人员的电脑上完成,而任何一台电脑上所能安装的浏览器是远远不能满足测试兼容性需求的,如果需要配备各种平台的电脑也是非常浪费人力物力。那么有没有工具能自动把本地的测试代码在所有平台上都跑一遍,并且追踪完整的测试过程,把 log 信息反馈回本地?有!那就是云测试工具。

目前国内外的云测试工具和方案有很多,因为云测试的实质是运行远程虚拟机,需要大量的服务器和齐全的设备,所以大多数都是收费的。这里推荐 SauceLabs,它虽然也是收费的,但是新注册的账号可以提供 8 个并发测试,免费 90 个小时的自动测试时长,而且操作简单,用户体验非常友好,很多著名的前端框架比如 Vue 等也在使用它进行兼容性测试。

这篇文章就详细介绍一下如何利用 SauceLabs 这个云测试工具进行跨平台的 JavaScript 单元测试,让你的代码可以轻松的通过所有版本的 Windows IE, Mac Safari 以及各种移动设备浏览器的测试评估,测试后还可以生成浏览器测试状态矩阵图:

browser-matrix.png

主流的 JavaScript 测试框架在 SauceLabs 上都有文档和配置说明,其中 Karma 是配置最为简单易懂的测试框架之一,下文就详细介绍下 Karma + SauceLabs 进行跨平台单元测试的整个流程,以及我自己在使用中遇到的一些问题和解决方法,如果你不用 Karma,可以在 这里 查找其他测试框架的配置方法。

以下步骤的前提是你已经写好了测试用例,如果你还不知道如何编写和组织单元测试,可以参考 js-test-workflows 的简单例子来开始学习 JavaScript 单元测试。

配置步骤

1. 安装 Karma

整个 SauceLabs 测试都是基于 Karma 配置文件 karma.sauce.js 完成的,通过插件 karma-sauce-launcher 自动完成远程服务器的连接和测试代码的提交。如果你的项目已经配备好了 Karma 和断言库,可以忽略此步骤。

  1. 安装测试框架 Karma

    npm install karma -g
  2. 安装测试断言库 jasmine (也可以选择其他库如 assert, should 等)

    npm install karma-jasmine

2. 安装 karma-sauce-launcher

这个工具是让 Karma 和 SauceLabs 建立连接的桥梁,它的作用是把本地的测试代码提交到云端测试虚拟机上,告诉 SauceLabs 要启动哪个系统哪个版本的浏览器来跑我们的测试代码,并且把测试结果、错误信息和追踪栈返回到本地的终端,一旦测试不通过,可以根据返回的信息来调试出错的代码。

npm install karma-sauce-launcher --save-dev

3. 注册 SauceLabs 并获取 accessKey

  1. SauceLabs 免费注册地址:https://saucelabs.com/signup/...

  2. 注册完成后,登陆账号,进入 Dashboard, 点击右下角的用户名,弹出菜单选择 User Setting 进入用户设置:

  3. 找到 USERNAME 和 Access Key:user-name.pngaccess-key

  4. 在项目目录下建立一个 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
    });
}

5. 启动 SauceLabs 测试

在终端输入:

karma start karma.sauce.js

本地的测试代码就会在 SauceLabs 云端跑起来了,注意的一点是,虽然免费用户只有 8 个并发的测试连接,但并不意味着一次只能测试 8 种浏览器,上面的 customLaunchers 一共定义了 13 中不同的浏览器,他们会先执行 8 个,空闲后再执行余下的,直至全部运行完毕。

云端测试完毕后的信息如下:

Chrome 54.0.2840 (Windows 7 0.0.0): Executed 143 of 143 SUCCESS (1.365 secs / 1.176 secs)
IE 10.0.0 (Windows 8 0.0.0): Executed 143 of 143 SUCCESS (1.313 secs / 1.118 secs)
IE 9.0.0 (Windows 7 0.0.0): Executed 143 of 143 SUCCESS (1.312 secs / 1.113 secs)
Firefox 47.0.0 (Mac OS X 10.10.0): Executed 143 of 143 SUCCESS (2.016 secs / 2.058 secs)
Chrome 54.0.2840 (Mac OS X 10.10.5): Executed 143 of 143 SUCCESS (1.724 secs / 1.485 secs)
Chrome 54.0.2840 (Windows 7 0.0.0): Executed 143 of 143 SUCCESS (1.365 secs / 1.176 secs)
IE 10.0.0 (Windows 8 0.0.0): Executed 143 of 143 SUCCESS (1.313 secs / 1.118 secs)
IE 9.0.0 (Windows 7 0.0.0): Executed 143 of 143 SUCCESS (1.312 secs / 1.113 secs)
Firefox 47.0.0 (Mac OS X 10.10.0): Executed 143 of 143 SUCCESS (2.016 secs / 2.058 secs)
Chrome 54.0.2840 (Mac OS X 10.10.5): Executed 143 of 143 SUCCESS (1.724 secs / 1.485 secs)
IE 11.0.0 (Windows 10 0.0.0): Executed 143 of 143 SUCCESS (2.584 secs / 1.262 secs)
Firefox 49.0.0 (Windows 7 0.0.0): Executed 143 of 143 SUCCESS (2.3 secs / 1.25 secs)
Safari 9.1.2 (Mac OS X 10.11.6): Executed 143 of 143 SUCCESS (1.164 secs / 1.173 secs)
Edge 14.14393.0 (Windows 10 0.0.0): Executed 143 of 143 SUCCESS (1.441 secs / 1.301 secs)
Chrome Mobile 39.0.0 (Android 5.1.0): Executed 143 of 143 SUCCESS (1.815 secs / 1.52 secs)
Android 4.2.0 (Android 4.2.0): Executed 143 of 143 SUCCESS (1.727 secs / 1.153 secs)
Mobile Safari 9.0.0 (iOS 9.3.0): Executed 143 of 143 SUCCESS (2.328 secs / 1.061 secs)

6. 获取测试状态和矩阵图

等所有浏览器都运行测试完毕后 SauceLabs 会生成状态和矩阵 svg 方便检查和公布代码测试状态,也可以在 https://saucelabs.com/u/YOUR_... 上查看状态结果。

  • 状态图标

    <a href="https://saucelabs.com/u/YOUR_SAUCE_USERNAME">
          <img src="https://saucelabs.com/buildstatus/YOUR_SAUCE_USERNAME" alt="Sauce Test Status"/>
    </a> 
  • 浏览器矩阵图

    <a href="https://saucelabs.com/u/YOUR_SAUCE_USERNAME">
          <img src="https://saucelabs.com/browser-matrix/YOUR_SAUCE_USERNAME.svg" alt="Sauce Test Status"/>
    </a>

    然后可以把这两个状态图标放到项目的 README 中,让人一看就知道目前项目的测试状态和浏览器兼容性了(SauceLabs 的 svg 的更新有点慢):

build-status.png

总结

简单总结下 SauceLabs 的优点:

  1. 提供几百种平台设备,基本上可以满足所有的兼容性测试需求;

  2. 配合 Karma 进行单元测试,操作简单易于部署;

  3. 提供测试、错误信息的及时反馈,便于查找错误;

  4. 免费使用 90 个小时的测试时长!

另外,分享下我自己使用 SauceLabs 遇到的几个问题:

  1. 偶尔遇到 Disconnect 和 Timeout 的情况,可能是由于国内网络原因,多试几次就好了。

  2. 配置参数里面的 captureTimeoutbrowserNoActivityTimeout 可以设得稍长些,有些连接超时也是因为这两个的默认时长太小了,另外如果不需要记录录像和图像,可以将 recordVideo recordScreenshots 设为 false 可以缩短运行时间。

  3. 如果总是遇到 Disconnect 的情况,一个就是可能你的代码有问题(有些错误警告在某些浏览器下是不会抛出的,比如 Safari)还有一个就是 Karma 的版本问题,v0.13.x 经常出现链接超时,升到最新版本即可。

  4. 对于错误产生的 Disconnect 并且没有抛错的情况,可以安装 karma-spec-launcher 并且把 Karma 的测试处理报告程序改成 reporters: ['spec', 'saucelabs'] 这样就可以在终端很清晰的看到运行到哪个测试用例导致的 Disconnect,方便排查出错的地方。

  5. 如果是开源项目,记得把 sauce.json 文件放到 .gitignore 中,不要暴露你的 AccessKey 到 git 提交中。


唐朝的唐
529 声望49 粉丝

代码洁癖、代码洁癖、代码洁癖