Jasmine 为 JavaScript 提供了 TDD (测试驱动开发)的框架,对于前端软件开发提供了良好的质量保证,这里对 Jasmine 的配置和使用做一个说明。

目前,Jasmine 的最新版本是 2.3 版,这里以 2.3 版进行说明。网上已经有一些关于 Jasmine 的资料,但是,有些资料比较久远,已经与现有版本不一致。所以,这里特别以最新版进行说明。

1. 下载

官网地址:http://jasmine.github.io/

官网文档地址:http://jasmine.github.io/2.3/introduction.html

下载地址:https://github.com/jasmine/jasmine/releases

在 GitHub 上提供了独立版本 jasmine-standalone-2.3.4.zip 和源码版本,如果使用的话,直接使用 standalone 版本即可。

解压之后,可以得到如下所示的文件结构。

其中,lib 中是 Jasmine 的实现文件,在 lib/jasmine-2.3.4 文件夹中,可以看到如下的文件。

打开最外层的 SpecRunner.html ,这是一个 Jasmine 的模板,其中提供了测试的示例,我们可以在使用中直接套用这个模板。其中的内容为:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title>Jasmine Spec Runner v2.3.4</title>
  6.  
  7. <link rel="shortcut icon" type="image/png" href="lib/jasmine-2.3.4/jasmine_favicon.png">
  8. <link rel="stylesheet" href="lib/jasmine-2.3.4/jasmine.css">
  9.  
  10. <script src="lib/jasmine-2.3.4/jasmine.js"></script>
  11. <script src="lib/jasmine-2.3.4/jasmine-html.js"></script>
  12. <script src="lib/jasmine-2.3.4/boot.js"></script>
  13.  
  14. <!-- include source files here... -->
  15. <script src="src/Player.js"></script>
  16. <script src="src/Song.js"></script>
  17.  
  18. <!-- include spec files here... -->
  19. <script src="spec/SpecHelper.js"></script>
  20. <script src="spec/PlayerSpec.js"></script>
  21.  
  22. </head>
  23.  
  24. <body>
  25. </body>
  26. </html>

可以看到其中引用了 lib/jasmine-2.3.4/jasmine.js, lib/jasmine-2.3.4/jasmine-html.js 和 lib/jasmine-2.3.4/boot.js 三个系统文件,其中 boot.js 是网页情况下的启动文件,在 张丹 的 jasmine行为驱动,测试先行 这篇文章中,要写一个 report.js 的启动脚本,这里已经不用了,直接使用 boot.js 就可以。

页面下面引用的  src/Player.js 和 src/Song.js 是我们的测试对象,而 spec/SpecHelper.js 和 spec/PlayerSpec.js 则是两个对应的测试文件,测试用例就定义在 spec 中。

2. 测试的定义

我们还是直接看示例,

一个是 Song.js,这里定义了一个 Song 的类,通过原型定义了一个persistFavoriteStatus 实例方法,注意,这里还没有实现,如果调用则会抛出异常。脚本如下。

  1. function Song() {
  2. }
  3.  
  4. Song.prototype.persistFavoriteStatus = function(value) {
  5. // something complicated
  6. throw new Error("not yet implemented");
  7. };

另外一个是 player.js,定义了 Player 类,定义了一个歌手,通过原型定义了 play, pause, resume 和 makeFavorite 实例方法。对象有一个 isPlaying 的状态,其中 resume 还没有完成。

  1. function Player() {
  2. }
  3. Player.prototype.play = function(song) {
  4. this.currentlyPlayingSong = song;
  5. this.isPlaying = true;
  6. };
  7.  
  8. Player.prototype.pause = function() {
  9. this.isPlaying = false;
  10. };
  11.  
  12. Player.prototype.resume = function() {
  13. if (this.isPlaying) {
  14. throw new Error("song is already playing");
  15. }
  16.  
  17. this.isPlaying = true;
  18. };
  19.  
  20. Player.prototype.makeFavorite = function() {
  21. this.currentlyPlayingSong.persistFavoriteStatus(true);
  22. };

下面看测试的定义,具体测试的说明,直接加在注释中。

  1. describe("Player", function() {
  2. var player;
  3. var song;
  4.  
  5. beforeEach(function() {
  6. player = new Player();
  7. song = new Song();
  8. });
  9.  
  10. // 检测正在歌手进行的歌曲确实是指定的歌曲
  11. it("should be able to play a Song", function() {
  12. player.play(song);
  13. expect(player.currentlyPlayingSong).toEqual(song);
  14.  
  15. //demonstrates use of custom matcher
  16. expect(player).toBePlaying(song);
  17. });
  18.  
  19. // 进行测试的分组,这里测试暂停状态
  20. describe("when song has been paused", function() {
  21. beforeEach(function() {
  22. player.play(song);
  23. player.pause();
  24. });
  25.  
  26. // isPlaying 的状态检测
  27. it("should indicate that the song is currently paused", function() {
  28. expect(player.isPlaying).toBeFalsy();
  29.  
  30. // demonstrates use of 'not' with a custom matcher
  31. //
  32. expect(player).not.toBePlaying(song);
  33. });
  34.  
  35. // 恢复
  36. it("should be possible to resume", function() {
  37. player.resume();
  38. expect(player.isPlaying).toBeTruthy();
  39. expect(player.currentlyPlayingSong).toEqual(song);
  40. });
  41. });
  42.  
  43. // demonstrates use of spies to intercept and test method calls
  44. // 使用 spyOn 为对象创建一个 mock 函数
  45. it("tells the current song if the user has made it a favorite", function() {
  46. spyOn(song, 'persistFavoriteStatus');
  47.  
  48. player.play(song);
  49. player.makeFavorite();
  50.  
  51. expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true);
  52. });
  53.  
  54. //demonstrates use of expected exceptions
  55. // 异常检测
  56. describe("#resume", function() {
  57. it("should throw an exception if song is already playing", function() {
  58. player.play(song);
  59.  
  60. expect(function() {
  61. player.resume();
  62. }).toThrowError("song is already playing");
  63. });
  64. });
  65. });

使用浏览器直接打开 SpenRunner.html 看到的结果

可以看到测试都通过了。

如果我们将第一个测试 expect(player.currentlyPlayingSong).toEqual(song); 改成 expect(player.currentlyPlayingSong).toEqual( 1 );

测试通不过,显示会变成这样。

3. 语法

3.1 describe 和 it

describe 用来对测试用例进行分组,分组可以嵌套,每个分组可以有一个描述说明,这个说明将会出现在测试结果的页面中。

  1. describe("Player", function() {
  2. describe("when song has been paused", function() {

而 it 就是测试用例,每个测试用例有一个字符串的说明,匿名函数内就是测试内容。

  1. // 检测正在歌手进行的歌曲确实是指定的歌曲
  2. it("should be able to play a Song", function() {
  3. player.play(song);
  4. expect(player.currentlyPlayingSong).toEqual(song);
  5. });

测试结果的断言使用 expect 进行,函数内提供测试的值,toXXX 中则是期望的值。

上面的测试使用 toEqual 进行相等断言判断。

3.2 beforeEach 和 afterEach

示例中还出现了 beforeEach。

  1. var player;
  2. var song;
  3.  
  4. beforeEach(function() {
  5. player = new Player();
  6. song = new Song();
  7. });

顾名思义,它表示在本组的每个测试之前需要进行的准备工作。在我们这里的测试中,总要用到 player 和 song 这两个对象实例,使用 forEach 保证在每个测试用例执行之前,重新对这两个对象进行了初始化。

afterEach 会在每一个测试用例执行之后执行。

3.3 自定义的断言

除了系统定义的 toEqual 等等断言之外,也可以使用自定义的断言,在上面的示例中就出现了 toBePlaying 断言。

  1. //demonstrates use of custom matcher
  2. expect(player).toBePlaying(song);

这个自定义的断言定义在 SpecHelper.js 文件中。

  1. beforeEach(function () {
  2. jasmine.addMatchers({
  3. toBePlaying: function () {
  4. return {
  5. compare: function (actual, expected) {
  6. var player = actual;
  7.  
  8. return {
  9. pass: player.currentlyPlayingSong === expected && player.isPlaying
  10. };
  11. }
  12. };
  13. }
  14. });
  15. });

其中调用了 jasmine 的 addMatchers 函数进行定义,原来这里不叫断言,称为 matcher ,也就是匹配器。

断言是一个函数,返回一个对象,其中有一个 compare 的函数,这个函数接收两个参数,第一个是实际值,第二个为期望的值。具体的断言逻辑自己定义,这里比较歌手演唱的对象是否为我们传递的对象,并且歌手的状态为正在表演中。

断言函数需要返回一个对象,对象的 pass 属性为一个 boolean 值,表示是否通过。

4. 常用断言

4.1 toEqual

深相等,对于对象来说,会比较对象的每个属性。对于数组来说,会比较数组中每个元素。

  1. describe("The 'toEqual' matcher", function() {
  2.  
  3. it("works for simple literals and variables", function() {
  4. var a = 12;
  5. expect(a).toEqual(12);
  6. });
  7.  
  8. it("should work for objects", function() {
  9. var foo = {
  10. a: 12,
  11. b: 34
  12. };
  13. var bar = {
  14. a: 12,
  15. b: 34
  16. };
  17. expect(foo).toEqual(bar);
  18. });
  19. });

4.2 toBe

对于对象,引用相等。对于值,值相等。

  1. pass: actual === expected

例如

  1. it("and has a positive case", function() {
  2. expect(true).toBe(true);
  3. });

4.3 toBeTruthy

是否为真。

  1. it("The 'toBeTruthy' matcher is for boolean casting testing", function() {
  2. var a, foo = "foo";
  3.  
  4. expect(foo).toBeTruthy();
  5. expect(a).not.toBeTruthy();
  6. });

4.4 toBeFalsy

是否为假。

  1. it("The 'toBeFalsy' matcher is for boolean casting testing", function() {
  2. var a, foo = "foo";
  3.  
  4. expect(a).toBeFalsy();
  5. expect(foo).not.toBeFalsy();
  6. });

4.5 toBeDefined

是否定义过

  1. it("creates spies for each requested function", function() {
  2. expect(tape.play).toBeDefined();
  3. expect(tape.pause).toBeDefined();
  4. expect(tape.stop).toBeDefined();
  5. expect(tape.rewind).toBeDefined();
  6. });

4.6 toBeUndefined

没有定义

  1. it("The `toBeUndefined` matcher compares against `undefined`", function() {
  2. var a = {
  3. foo: "foo"
  4. };
  5.  
  6. expect(a.foo).not.toBeUndefined();
  7. expect(a.bar).toBeUndefined();
  8. });

4.7 toBeNull

  1. it("The 'toBeNull' matcher compares against null", function() {
  2. var a = null;
  3. var foo = "foo";
  4.  
  5. expect(null).toBeNull();
  6. expect(a).toBeNull();
  7. expect(foo).not.toBeNull();
  8. });

4.9 toBeGreaterThan

  1. it("The 'toBeGreaterThan' matcher is for mathematical comparisons", function() {
  2. var pi = 3.1415926,
  3. e = 2.78;
  4.  
  5. expect(pi).toBeGreaterThan(e);
  6. expect(e).not.toBeGreaterThan(pi);
  7. });

4.10 toBeLessThan

  1. it("The 'toBeLessThan' matcher is for mathematical comparisons", function() {
  2. var pi = 3.1415926,
  3. e = 2.78;
  4.  
  5. expect(e).toBeLessThan(pi);
  6. expect(pi).not.toBeLessThan(e);
  7. });

4.11 toBeCloseTo

  1. it("The 'toBeCloseTo' matcher is for precision math comparison", function() {
  2. var pi = 3.1415926,
  3. e = 2.78;
  4.  
  5. expect(pi).not.toBeCloseTo(e, 2);
  6. expect(pi).toBeCloseTo(e, 0);
  7. });

4.12 toContain

集合中是否包含。

  1. it("The 'toContain' matcher is for finding an item in an Array", function() {
  2. var a = ["foo", "bar", "baz"];
  3.  
  4. expect(a).toContain("bar");
  5. expect(a).not.toContain("quux");
  6. });

4.13 toMatch

正则表达式的匹配

  1. it("The 'toMatch' matcher is for regular expressions", function() {
  2. var message = "foo bar baz";
  3.  
  4. expect(message).toMatch(/bar/);
  5. expect(message).toMatch("bar");
  6. expect(message).not.toMatch(/quux/);
  7. });

4.14 toThrow

检测是否抛出异常

  1. it("The 'toThrow' matcher is for testing if a function throws an exception", function() {
  2. var foo = function() {
  3. return 1 + 2;
  4. };
  5. var bar = function() {
  6. return a + 1;
  7. };
  8.  
  9. expect(foo).not.toThrow();
  10. expect(bar).toThrow();
  11. });

4.15 toHaveBeenCalled

4.16 toHaveBeenCalledWith

是否调用过。

  1. describe("A spy", function() {
  2. var foo, bar = null;
  3.  
  4. beforeEach(function() {
  5. foo = {
  6. setBar: function(value) {
  7. bar = value;
  8. }
  9. };
  10.  
  11. spyOn(foo, 'setBar');
  12.  
  13. foo.setBar(123);
  14. foo.setBar(456, 'another param');
  15. });
  16.  
  17. it("tracks that the spy was called", function() {
  18. expect(foo.setBar).toHaveBeenCalled();
  19. });
  20.  
  21. it("tracks all the arguments of its calls", function() {
  22. expect(foo.setBar).toHaveBeenCalledWith(123);
  23. expect(foo.setBar).toHaveBeenCalledWith(456, 'another param');
  24. });
  25.  
  26. it("stops all execution on a function", function() {
  27. expect(bar).toBeNull();
  28. });
  29. });

RequireJS Jasmine 2.0 编写测试

http://ju.outofmemory.cn/entry/96587

使用 Jasmine 进行测试驱动的 JavaScript 开发的更多相关文章

  1. TDD测试驱动的javascript开发(3) ------ javascript的继承

    说起面向对象,人们就会想到继承,常见的继承分为2种:接口继承和实现继承.接口继承只继承方法签名,实现继承则继承实际的方法. 由于函数没有签名,在ECMAScript中无法实现接口继承,只支持实现继承. ...

  2. 构建简单的Maven工程,使用测试驱动的方式开发项目

    构建简单的Maven工程很简单,这里写这篇随笔的原因是希望自己能记住几个小点. 一.安装Maven 1.下载maven:https://maven.apache.org/download.cgi 2. ...

  3. 使用模拟对象(Mock Object)技术进行测试驱动开发

    敏捷开发 敏捷软件开发又称敏捷开发,是一种从上世纪 90 年代开始引起开发人员注意的新型软件开发方法.和传统瀑布式开发方法对比,敏捷开发强调的是在几周或者几个月很短的时间周期,完成相对较小功能,并交付 ...

  4. Scrum敏捷软件开发之技术实践——测试驱动开发TDD

    重复无聊的定义 测试驱动开发,英文全称Test-Driven Development,简称TDD,是一种不同于传统软件开发流程的新型的开发方法.它要求在编写某个功能的代码之前先编写测试代码,然后只编写 ...

  5. 关于测试驱动的开发模式以及实战部分,建议看《Python Web开发测试驱动方法》这本书

    关于测试驱动的开发模式以及实战部分,建议看<Python Web开发测试驱动方法>这本书

  6. 软件工程 - Test-Driven Development (TDD),测试驱动开发

    参考 https://baike.baidu.com/item/%E6%B5%8B%E8%AF%95%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/3328831?fr=al ...

  7. JavaScript开发工具大全

    译者按: 最全的JavaScript开发工具列表,总有一款适合你! 原文: THE ULTIMATE LIST OF JAVASCRIPT TOOLS 译者: Fundebug 为了保证可读性,本文采 ...

  8. 为何说 JavaScript 开发很疯狂

    [编者按]本文作者为 Sean Fioritto,主要阐述了 JavaScript 开发为何让人有些无从下手的根本原因.文章系国内 ITOM 管理平台 OneAPM 编译呈现. 网络开发乐趣多多!Ja ...

  9. 2013年JavaScript开发人员调查结果

    JavaScript开发人员调查现在已经结束,一如既往社区对结果进行了进一步分析: 总结(汉语) 原始数据(电子表格) 2012年结果 51%的被参与者写客户端代码,而28%的人说他们编写服务器端代码 ...

随机推荐

  1. awk脚本

    $0,意即所有域. 有两种方式保存shell提示符下awk脚本的输出.最简单的方式是使用输出重定向符号>文件名,下面的例子重定向输出到文件wow. #awk '{print $0}' grade ...

  2. Git command line

    # Pull the repo from master git pull # Create branch for myself in local git branch john/jenkins_cod ...

  3. 微信H5页面分享

    #jssdk.php <?php class JSSDK { private $appId; private $appSecret; public function __construct($a ...

  4. AFNetworking 3.0+ 启用完整、严格的https证书较验参考代码

    // 1.初始化单例类      AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];     manager.securit ...

  5. 如何安装mysql服务

    我刚开始安装mysql的时候,在windows的服务里面可以看到,但是装了以后有一段时间没有用它了,我在准备从windows的服务里面启动mysql服务的时候,发现没有mysql的服务了,那我的解决办 ...

  6. JAVA:NIO初步了解

    简介: Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式. Java NIO: Ch ...

  7. bootstrap入门-1.可视化布局

    下载地址:http://v3.bootcss.com/getting-started/#download   HTML模板: <!DOCTYPE html> <html> &l ...

  8. Leetcode: Strong Password Checker

    A password is considered strong if below conditions are all met: It has at least 6 characters and at ...

  9. 0-systemctl开机启动项

    防火墙:iptables Apache服务名称:httpd MySQL服务名称:mysqld VSFTP服务名称:vsftpd <!--CentOS7新指令--> 使某服务 自动启动 sy ...

  10. QQ 图片

    http://wpa.qq.com/pa?p=2:QQ号码:45 查看QQ是否在线,或者图片,在这里,其他的另行百度. <!-- tencent://message/?uin=763999883 ...