文档结构  
可译网翻译有奖活动正在进行中,查看详情 现在前往 注册?
原作者:Sharon Sudhan (2016-08-11)    来源:CodeProject [英文]
CY2    计算机    2016-08-13    0评/339阅
翻译进度:已翻译   参与翻译: hc1903 (11)

Jasmine 简介

Jasmine 是一个基于行为驱动开发的开源自动化测试框架,它可以帮助你来测试你的JavaScript代码。它可以在任何或者JavaScript能运行的平台上运行和执行。Jasmine 不依赖任何浏览器、DOM 或者任何其它的JavaScript框架就能运行。但是Jasmine和其它比如说requireJS和JSCover等框架组合在一起时可以让你的测试更加有效。

BDD

Behavior-driven development(行为驱动开发)是利用测试驱动开发和域驱动设计相结合的一种开发技术。它解释了语句和用户故事的场景,而不是功能测试。因此它有助于创建一个开发者、企业或者任何利益相关者都很容易理解的开发过程。它通过功能、技术活动和共享项目资源来提升开发人员、测试人员和分析人员之间的协作能力。至于Jasmine还能在BDD分类下走多远还是一个有争议的问题。

第 1 段(可获 1.83 积分)

安装 Jasmine

安装Jasmine并不是一项困难的工作。你可以从这里下载最新的离线版本。你只需要写好测试文件并正确地引用Jasmine库、测试文件和Spec runner里的脚本就可以建立一个独立的Jasmine版本。Spec runner是一个HTML文件,它包含了我们必须按顺序引用的Jasmine库,JavaScript文件和Jasmine测试脚本。打开spec runner之后它就会进行测试,并且你可以立即看到测试结果。

编写第一个测试文件

假设我有一个helloworld.js的文件并且我要为一个可以接收两个数字并返回它们总和的函数addNumbers做测试。开始写测试脚本之前我们要写创建一个JavaScript文件和一个测试套件。我把这个JS文件命名为helloworldTest.js。选择你顺手的编辑器打开这个文件然后就可以开始写测试脚本了。首先我们要为测试用例创建测试套件。一个文件中可以有多个测试套件。下面就是创建测试套件的代码,一个测试用例应该被包含在测试套件内部。

第 2 段(可获 2.53 积分)
describe('test suite name',function(){
}

这就是用来创建一个测试套件的声明语法。套件中的第一个参数是被引号包含的套件名称,这个名字应该明确地指定测试套件的目的。在名称后面是一个函数,我们可以在这个函数的回调里写测试脚本。

describe('Addition specs', function(){
  it('should add 2 valid numbers and returns valid result', function(){
    expect(addNumbers(1, 1)).toEqual(2);
  });
});
要运行这个文件,就要先引用Jasmine库和待测试文件,并且在spec runner中打开测试套件,然后就可以开始测试了,测试的结果会在一个页面上展示出来。
第 3 段(可获 1.1 积分)
<title>Spec Runner</title>

  <!-- load jasmine -->
  <link rel="stylesheet" type="text/css" href="tools/Jasmine/jasmine.css">
  <script type="text/javascript" src="tools/Jasmine/jasmine.js"></script>
  <script type="text/javascript" src="tools/Jasmine/jasmine-html.js"></script>

  <!-- include source files here... -->
  <script type="text/javascript" src="src/HelloWorld.js"></script>

  <!-- include spec files here... -->
  <script type="text/javascript" src="specs/HelloWorldTest.js"></script>

匹配器

在上一个例子中我们用toEqual()来比较了使用addNumbers(1, 1)方法来得到1+1等于2的结果。这里的toEqual()就是一个匹配器。Jasmine内置了一些匹配器,下面是其中的一部分。所有的匹配器都会返回一个布尔值,然后Jasmine根据这些结果来决定此次测试的成功与否。

.toMatch()
.toBeUndefined()
.toBeTruthy()
.toBeFalsy()
.toContain()
.toBeLessThan()
.toBeGreaterThan()
.toThrow()
.toBeCloseTo()
.toBe()

我们可以通过给这些匹配器添加前缀.not来取相反的值。例如:

.not.toBeTruthy()

如果这些内置的匹配器无法满足你的需求,你也可以自己创建一些匹配器。

自定义匹配器

有了自定义匹配器这个特性我们就可以创建符合自己需求的匹配器来替代Jasmine的内置匹配器。下面的代码演示了怎样创建一个可以检测一个数字是否大于5的自定义匹配器。

第 4 段(可获 1.65 积分)
this.addMatchers({
  IsGreaterthanFive: function () {
  return this.actual>5 }
});

现在你可以用刚刚创建的匹配器来编写测试文件了。

beforeEach 和 afterEach

如果你想要在测试开始之前就执行一些代码,将这个需要在每一个测试文件里都写一遍无疑是痛苦并且不可取的,为了避免这个尴尬的境地并且复用你的代码,你可以把这些代码写在beforeEach方法里面。这样一来不管你在测试套件下执行多少个测试,beforeEach内部的代码总会在测试开始前就执行。这就方便了我们根据需要来设置一些默认值。

第 5 段(可获 1.16 积分)
var count=0;
beforeEach(function()
{
  count=count+1;
  console.log("Started executing test no : " + count);
});

如果beforeEach是在每个测试开始之前执行,那么你可以猜测到afterEach是在每个测试完成之后才会开始执行的。所以这个方法可以用来清除掉那些因为之前的测试而产生的结果,这也取决于你实际的需求。

afterEach(function()
{
console.log(Completed executing test no : " + count);
});

监听(Spy)

应用程序的其中一个功能的实现可能会依赖于另外一些功能。为了测试一个功能,我们可能不需要调用那些不在测试范围内的依赖项,有一些依赖项比如说常项的调用代价很小,但是一些影响因素很多的像Ajax这样依赖网络延迟的依赖项调用起来可能会需要很长时间。如果一个Ajax请求是依赖于一个实际的方法并且你不想让它在测试中进行,那么你就不需要直接调用它,你可以用spy这个方法来替代直接的调用。

第 6 段(可获 1.93 积分)

SpyOn

如果我们要监听一个已经存在有对象,可以用spyOn方法来实现。

spyOn(Obj, "getSumofNumbers")

spyOn的.add对象提供了一些可以决定spy应该怎样运行的选项。

我们可以

callThrough() : 我们可以结合callThrough()来监视对象并跟踪所有调用,然后委托给实际实现. 因此,实际调用将被调用.。

spyOn(Obj, "getSumofNumbers").and.callThrough();

returnValue() 我们可以通过returnValue()来很容易地返回任何我们需要的东西来作为函数的输出值。在以下的例子中所有调用getRandomNumbers方法的对象都将返回231作为输出。

第 7 段(可获 1.2 积分)
spyOn(Obj, "getRandomNumber").and.returnValue(231);

CallFake():callFake() 只是一个可以在callFake函数内调用回调函数的方法。

it('should add 2 valid numbers and returns fake method result',function(){
 spyOn(window,'getSumofNumbers').and.callFake(function(){return 5;});
 expect(getSumofNumbers(1,2)).toEqual(5);
//see here 1+2 is not returning actual value 3 instead it gives us //the faked result 5.
});

Stub() :  Stub()方法用于创建一个假的实际方法. 当使用callThough时它其实是用被监听的对象来运行实际方法的,它本身并没有运行实际方法。当我们测试一个函数是否被调用的时候它非常有用,但是它不能测试方法的行为。

第 8 段(可获 0.93 积分)
it('should stub returns an undefined obect',function(){
  spyOn(window,'getSumofNumbers').and.callFake(function(){return  5;});
  window.getSumofNumbers.and.stub();
  expect(getSumofNumbers(1,2)).toBe(undefined);
  });
it('should check whether function call is invoked',function(){
  spyOn(window,'getSumofNumbers').and.callFake(function(){return 5;});
  window.getSumofNumbers.and.stub();
  var x = window.getSumofNumbers(1,2);
  expect(window.getSumofNumbers).toHaveBeenCalled(); 
  });

CreateSpy

CreateSpy通常用于在一个没有被包含在测试范围内或者没有被其它方法监听的地方来创建一个监听方法。这些方法不会被真正地执行,所以我们用CreateSpy方法来让一些函数返回我们需要的结果。

第 9 段(可获 0.65 积分)

如果你想要你的监听方法返回一个值,你可以用andReturn(value)来告诉Jasmine你需要返回什么。

user.getDept = jasmine.createSpy('getDept()').andReturn('IT');

如果你想要定义一个方法,我们可以用andCallFake()方法来在函数内部实现。

user.getDept = jasmine.createSpy('getDept()'). andCallFake( function(){ 
var Dept= 'I'+'T';
Return Dept;
});

CreateSpyObj

CreateSpyObj 被用来创建一个以我们已有的每个监听方法的名称作为属性的一个对象。

car = jasmine.createSpyObj('car', ['accelerate','break','cluch',]);
So created spy's will have all the functionalities of spy function 
   
car.break.andReturn('break now');
car.accelerate.andCallFake(function() {return 'accelerated'});

忽略测试套件

我们已经了解了用describe函数来定义一个测试套件。如果因为某个特殊的原因我们不想运行某个测试套件并且展现测试结果,那么我们可以在给describe添加前缀x来把这个套件标记为忽略套件。

xdescribe('ignored test suite',function(){/* some code here */});

 

第 10 段(可获 1.38 积分)

忽略测试

和忽略测试套件相似,我们同样可以用在it前添加x来忽略一个用户测试。

xit('ignored test',function(){ /* some code here */})

总结

单元测试在编程解决方案中是不可避免的,Jasmine帮助我们弥补了有效客户端测试框架稀少的缺口。尽管它是很有效的,但是当我们写测试用例的时候仍然需要合理地运用一些语法比如Spying的灵活性以免它们影响测试质量。

希望这篇简短的文章可以帮助你理解怎样来为JavaScript代码写一个很棒的测试用例。

参考文档

Jasmine的源文件已经在GitHub上开源。

获取Jasmine : http://jasmine.github.io/

文档 : http://jasmine.github.io/2.0/introduction.html

第 11 段(可获 1.39 积分)

文章评论