在前端开发中,大部分时间都是使用人肉加上 console.log
或者 debuger
进行测试,效率及测试质量都是因人而异,加上JavaScript语言本身缺少类型检查,编译期间无法定位到错误,还有常见兼容性问题,都是影响前端开发常见问题。为提高开发效率,减少或者避免人工干预,就需要编写测试用例进行自动化测试。在文章《JavaScript单元测试的“抹茶”组合:Mocha和Chai》介绍了JavaScript 的单元测试,并在文章《Chai 和 Mocha 为API编写测试》展示了一个简单的实例,本文再来介绍一下自动化测试及工具Karma。
Karma 为前端自动化测试提供了跨浏览器测试的能力,它集成了像 Jasmine
(基于 BDD 的测试框架),PhantomJS
(无界面的浏览器) 这些测试套件。还有一些其他有用的功能,比如生成代码覆盖率的报告等。
TDD(Test Drivin Development)
是测试驱动开发,强调的是一种开发方式,以测试来驱动整个项目,即先根据接口完成测试编写,然后在完成功能时要不断通过测试,最终目的是通过所有测试。BDD(Behavior Drivin Development)
行为驱动开发,可以理解为是TDD
的分支,即也是测试驱动,但BDD
强调的是写测试的风格,即测试要写得像自然语言,运用一些比如expect
、should
等跟自然语言相近的断言,让项目的各个成员甚至产品都能看懂测试,甚至编写测试。
简介
Karma - Spectacular Test Runner for Javascript(基于node.js的Javascript测试运行环境)
Karma 是由Google团队开发的一套前端测试运行框架,karma
会启动一个web服务器,将js源代码和测试脚本放到 PhantomJS
或者 Chrome
上执行。
On the AngularJS team, we rely on testing and we always seek better tools to make our life easier. That's why we created Karma - a test runner that fits all our needs.
该工具可用于测试所有主流Web浏览器,也可集成到CI工具,也可和其他代码编辑器(例如VsCode)一起使用。这个测试工具的一个强大特性就是,它可以监控(Watch)文件的变化,然后自行执行,通过 console.log
显示测试结果。主要提供以下功能:
- 提供真实环境,可以配置各种chrome、firefox等各种浏览器环境或者
Phantomjs
等无头浏览器环境 - 可控制自动化测试流程,比如编辑器保存时自动全部全部测试用例
- 强大适配器,可以在
karma
上面配置jasmine
,mocha
等单元测试框架。 - 配置方便
安装
npm install karma -g
测试框架
Jasmine(基于 TDD 的测试框架)
jasmine 是一个行为驱动开发(BDD)测试框架, 一个 JavaScript 测试框架,它不依赖于浏览器、dom或其它 JavaScript 框架,其语法十分简单。
分组 describe()
describe 的用于群组相关的测试,接受两个参数:string
和 function
。
string
是这个测试套件的标题function
就是实现这个测试套件。可以嵌套多个Distribution
describe("a suite", function () { //这是一个测试分组 it("with an expactation", function () { expect(true).toBe(true); }); //可以添加多个测试 });
测试 it()
it
代表具体的测试,当其中所有的断言都为true时,则该测试通过;否则测试失败
describe("a suit is just a function", function () { var a = 10; it("and here is a test", function () { var a = true; expect(a).toBe(true); }); });
期望 expect()
通过链式 Matcher 方法来比较实际值是否和预期值一致,如果一致就是测试通过。
desribe('the "toBe" matcher compares with "===" ', function () { it("positive expect", function () { expect(true).toBe(true); }); it("negative expect", function () { expect(false).not.toBe(true); }); });
Matchers
都是作为期望的链式调用而使用,用于比较期望值和实际值。其中可以使用 not
进行期望结果的否定。
每个匹配方法在期望值和实际值之间执行逻辑比较,它负责告诉 jasmine
断言的真假,从而决定测试的成功或失败。
- 肯定断言
expect(true).toBe(true)
; - 否定断言
expect(false).not.toBe(true)
;
jasmine
有很丰富的匹配方法,而且可以自定义匹配方法。 内置的匹配方法有:
toBe()
describe("included matchers", function () { it('"toBe" matcher compares width === ', function () { const a = 12; const b = a; expect(a).toBe(b); expect(a).not.toBe(null); }); });
toEqual()
describe("'toEqual' matcher", function () { it("适合简单的文本和变量", function () { const a = 12; expect(a).toEqual(12); }); it("也适用于对象的深度比较", function () { const foo = { a: 12, b: 34, }; const bar = { a: 12, b: 34, }; expect(foo).toEqual(bar); }); });
toMatch()
it("'toMatch' matcher 是使用正则表达式", function () { const message = "foo bar baz"; expect(message).toMatch(/bar/); expect(message).toMatch("bar"); expect(message).not.toMatch(/quux/); });
toBeDefined()
it("'toBeDefined' matcher 是将期望和undefined进行比较", function () { const a = { foo: "foo", }; expect(a.foo).toBeDefined(); expect(a.bar).not.toBeDefined(); });
toBeNull()
it("'toBeNull' matcher 是将期望和null进行比较", function () { const a = null; const foo = "foo"; expect(null).toBeNull(); expect(a).toBeNull(); expect(foo).not.toBeNull(); });
toBeTruthy()
it("'toBeTruthy' matcher 是进行Boolean转换后与true进行比较", function () { const a, foo = "foo"; expect(foo).toBeTruthy(); expect(a).not.toBeTruthy(); });
toContain()
describe("'toContain' matcher", function () { it("适用于数组的寻值", function () { const a = ["foo", "bar", "baz"]; expect(a).toContain("bar"); expect(a).not.toContain("quux"); }); it("也使用于字符串内找单词", function () { const a = "foo bar baz"; expect(a).toContain("bar"); expect(a).not.toContain("quux"); }); });
toBeLessThan()
it("'toBeLessThan' matcher 进行小于的数值期望比较", function () { const pi = 3.1415926, e = 2.78;
expect(e).toBeLessThan(pi); expect(pi).not.toBeLessThan(e);
});
toThrowError()
it("'toThrowError' matcher 用于测试特定抛出异常", function () { const foo = function () { throw new TypeError("foo bar baz"); }; expect(foo).toThrowError("foo bar baz"); expect(foo).toThrowError(/bar/); expect(foo).toThrowError(TypeError); expect(foo).toThrowError(TypeError, "foo bar baz"); });
PhantomJS(无界面的浏览器)
PhantomJS是由Ariya Hidayat创建的,支持JavaScript API的无界面、运行在服务端的WebKit环境。
- 无界面网站测试
- 屏幕快照
- 页面操作自动化
- 网络监控
基于Jasmine实例
要使用 Karma 对代码进行单元测试,首先需要安装一系列的相关插件。新建一个名为 karm-test
的目录,并安装相关的插件:
npm install karma jasmine-core karma-jasmine karma-phantomjs-launcher -D
接下来对工程进行初始化:
karma init
之后会弹出一些选项,其中包含了一些初始化的配置工作,使用上下方向键可以在配置项之间进行切换。
初始化完成之后,会在项目中生成一个 karma.conf.js
文件,这个文件就是 Karma 的配置文件。 配置文件比较简单,能够比较轻松的看懂,这里对原始的配置文件进行简单的修改,结果如下:
module.exports = function (config) { config.set({ // base path that will be used to resolve all patterns (eg. files, exclude) basePath: "", // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter frameworks: ["jasmine"], // list of files / patterns to load in the browser files: ["./src/**/*.js", "./test/**/*.spec.js"], // list of files to exclude exclude: [], // preprocess matching files before serving them to the browser // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor preprocessors: {}, // test results reporter to use // possible values: 'dots', 'progress' // available reporters: https://npmjs.org/browse/keyword/karma-reporter reporters: ["progress"], // web server port port: 9876, // enable / disable colors in the output (reporters and logs) colors: true, // level of logging // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG logLevel: config.LOG_INFO, // enable / disable watching file and executing tests whenever any file changes autoWatch: false, // start these browsers // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher browsers: ["PhantomJS"], // Continuous Integration mode // if true, Karma captures browsers, runs the tests and exits singleRun: true, // Concurrency level // how many browser should be started simultaneous concurrency: Infinity, }); };
然后创建一个 src
目录和一个 test
目录,在其中分别创建 index.js
和 index.spec.js
文件。 要做的测试内容比较简单,对 index.js
中的两个函数(一个加法函数,一个乘法函数)进行测试。
src/index.js
文件如下:
// 加法函数 function add(x){ return function(y){ return x + y; } } // 乘法函数 function multi(x){ return function(y){ return x * y + 1; } }
test/index.spec.js
文件如下:
describe("运算功能单元测试",function(){ it("加法函数测试",function(){ const add5 = add(5) expect(add5(5)).toBe(10) }); it("乘法函数测试",function(){ const multi5 = multi(5) expect(multi5(5)).toBe(25) }) })
单测的代码写好后,就可以使用 karma start
来运行单元测试。由于乘法代码中有错误,因此测试结果是这样的:
将乘法函数的代码改为正常,再次启用 karma start
进行测试:
完
希望能够对你有所帮助,谢谢。