前言
之前看了一些关于Jasmine的文章,后来在官网上看到了文档,试着自己翻译了一下,有问题的欢迎大家指正哈。
官网地址为:Jasmine官方文档
介绍
Jasmine是一种用于测试JavaScript代码的行为驱动开发框架。它不依赖于其他任何JavaScript框架。它也不需要一个DOM结构。它有一个干净、清晰的语法,所以你可以很轻易地书写单元测试代码。本指南是针对于Jasmine2.4.1版本。
describe("A suite", function() {
it("contains spec with an expectation", function() {
expect(true).toBe(true);
});
});
单独的分配
发布页面有下载独立配置的链接,包括任何用于运行Jasmine的东西。在下载一个特定版本和压缩包之后,打开SpecRunner.html
启动所包含的specs。你会发现所需要的资源文件和他们各自的specs都在SpecRunner.html
里的<head>
标签里引入。
Suites(测试程序组):describe
Suites直接调用Jasmine的全局函数describe()
并传递两个参数:string
和function
。字符串表示被测试的Suites的名称或者标题。而函数则是需要实现Suites的代码块。
Specs(测试程序体):it
Specs通过调用全局函数it()
来定义,跟describe类似的传递两个参数:string
和function
。字符串是spec的标题,function
是测试程序或者测试代码。Spec包括一个或多个测试代码的状态期望值(expectations)。Expectations在Jasmine中是一个要么是真要么是假的断言。只有当spec中的断言都为真才可以通过这个测试程序,否则测试返回failing。
It’s Just Functions
既然describe()
和it()
是函数体,它们就可以包含所有可执行的代码去运行单体测试。JavaScript范围规则适用,所以在describe()
声明的变量在其Suites块内都是可用的。
describe("A suite is just a function", function() {
var a;
it("and so is a spec", function() {
a = true;
expect(a).toBe(true);
});
});
Expectations:expect
Expectations是函数expect()
建立的,而expect()函数传递一个称为actual(实际值)的参数。Expectations链式的连接着传递参数expected(期望值)的Matcher函数。
Matchers
每一个Matchers执行实际值(actual
)和期望值(expected
)之间的布尔比较。它负责反馈Expectation是否为真给Jasmine,而Jasmine根据这个布尔值比较来决定是否通过这个测试(Suites)。
任何Matcher都能通过在expect
调用Matcher前加上not
来实现一个否定的断言(expect(a).not().toBe(false);
)。
代码如下:
describe("The 'toBe' matcher compares with ===", function() {
it("and has a positive case", function() {
expect(true).toBe(true);
});
it("and can have a negative case", function() {
expect(false).not.toBe(true);
});
});
包含的Matchers
Jasmine有一套功能丰富的Matchers。当一个产品需要调用库里不包括的特殊的断言时,也可以自己定义Matchers。
自定义Matchers
常见的Matchers使用代码如下:
describe("Included matchers:", function() {
it("The 'toBe' matcher compares with ===", function() {
//toBe()类似于"==="
var a = 12;
var b = a;
expect(a).toBe(b);//通过
expect(a).not.toBe(null);//通过,因为a!==null
});
describe("The 'toEqual' matcher", function() {
//toEqual()类似于"=="
it("works for simple literals and variables", function() {
var a = 12;
expect(a).toEqual(12);//通过
});
it("should work for objects", function() {
var foo = {
a: 12,
b: 34
};
var bar = {
a: 12,
b: 34
};
expect(foo).toEqual(bar);//通过,两个对象属性和值都一样
});
});
it("The 'toMatch' matcher is for regular expressions", function() {
//toMatch()用于匹配正则表达式
var message = "foo bar baz";
expect(message).toMatch(/bar/);//通过,参数可以是正则表达式
expect(message).toMatch("bar");//通过,参数可以是字符串
expect(message).not.toMatch(/quux/);//通过,因为toMatch()匹配不到/quux/
});
it("The 'toBeDefined' matcher compares against `undefined`", function() {
//toBeDefined()判断参数是否定义
var a = {
foo: "foo"
};
expect(a.foo).toBeDefined();
expect(a.bar).not.toBeDefined();
});
it("The `toBeUndefined` matcher compares against `undefined`", function() {
//toBeUndefined()判断参数是否为undefined
var a = {
foo: "foo"
};
expect(a.foo).not.toBeUndefined();
expect(a.bar).toBeUndefined();
});
it("The 'toBeNull' matcher compares against null", function() {
//toBeNull()判断参数是否为空
var a = null;
var foo = "foo";
expect(null).toBeNull();
expect(a).toBeNull();
expect(foo).not.toBeNull();
});
it("The 'toBeTruthy' matcher is for boolean casting testing", function() {
//toBeTruthy()判断参数转化为布尔值时是否为true
var a, foo = "foo";
expect(foo).toBeTruthy();//通过,foo变量转变为true
expect(a).not.toBeTruthy();
});
it("The 'toBeFalsy' matcher is for boolean casting testing", function() {
//toBeFalsy()判断参数转化为布尔值时是否为false
var a, foo = "foo";
expect(a).toBeFalsy();
expect(foo).not.toBeFalsy();
});
it("The 'toContain' matcher is for finding an item in an Array", function() {
//toContain()判断元素是否存在于数组内。不适用于对象
var a = ["foo", "bar", "baz"];
expect(a).toContain("bar");
expect(a).not.toContain("quux");
});
it("The 'toBeLessThan' matcher is for mathematical comparisons", function() {
//toBeLessThan()判断实际值是否小于期望值
var pi = 3.1415926,
e = 2.78;
expect(e).toBeLessThan(pi);
expect(pi).not.toBeLessThan(e);
});
it("The 'toBeGreaterThan' matcher is for mathematical comparisons", function() {
//toBeGreaterThan()判断实际值是否大于期望值
var pi = 3.1415926,
e = 2.78;
expect(pi).toBeGreaterThan(e);
expect(e).not.toBeGreaterThan(pi);
});
it("The 'toBeCloseTo' matcher is for precision math comparison", function() {
//toBeCloseTo()数值比较时定义精度,先四舍五入后再比较
var pi = 3.1415926,
e = 2.78;
expect(pi).not.toBeCloseTo(e, 2);
expect(pi).toBeCloseTo(e, 0);
});
it("The 'toThrow' matcher is for testing if a function throws an exception", function() {
//toThrow()判断函数是否会抛出一个错误
var foo = function() {
return 1 + 2;
};
var bar = function() {
return a + 1;
};
expect(foo).not.toThrow();
expect(bar).toThrow();
});
it("The 'toThrowError' matcher is for testing a specific thrown exception", function() {
//toThrowError()判断函数是否抛出一个特别的错误,以下四种都能够通过测试
var 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");
});
});
使用fail函数手动失败一个Spec
fail()
函数可以使一个spec测试失败,可以传递一个失败信息或者错误对象给函数作为参数。例如:
describe("A spec using the fail function", function() {
var foo = function(x, callBack) {
if (x) {
callBack();
}
};
it("should not call the callBack", function() {
foo(false, function() {
fail("Callback has been called");
});
});
});
返回结果为:
用describe
集合相关联的测试程序体
describe()
函数将相关了的测试程序体集合起来,参数string
是为集合起来的程序体所取的名字,并且是连接所有specs给每个spec取一个全称。这个字符串使得能够在一个大型Suite中找到这个spec。例如:
describe("A spec", function() {
it("is just a function, so it can contain any code", function() {
var foo = 0;
foo += 1;
expect(foo).toEqual(1);
});
it("can have more than one expectation", function() {
var foo = 0;
foo += 1;
expect(foo).toEqual(1);
expect(true).toEqual(true);
});
});
代码向我们展示了一个describe()
函数就是几个spec的集合,并且每个spec里可以包括不止一个断言。
安装与拆卸
为了帮助一个测试体能够DRY(Don’t repeat yourself)任何重复的初始化和销毁代码,Jasmine提供了四个全局函数:beforeEach()
,afterEach()
,beforeAll()
,afterAll()
。
顾名思义,beforeEach()
函数在每一个spec调用时调用,先进行初始化操作,而afterEach()
则是在每一个spec调用之后调用。例如:
describe("A spec using beforeEach and afterEach", function() {
var foo = 0;
beforeEach(function() {
foo += 1;
});
afterEach(function() {
foo = 0;
});
it("is just a function, so it can contain any code", function() {
expect(foo).toEqual(1);
});
it("can have more than one expectation", function() {
expect(foo).toEqual(1);
expect(true).toEqual(true);
});
});
代码中两个spec的代码有一点点不同,在上级作用域(describe
)定义的参数,在后面都可以对其进行修改赋值。每次spec执行时都初始化beforeEach()
的内容,之后在下一次继续执行前,调用afterEach()
将变量重置。
beforeAll()
函数在describe()
函数运行时,并且在所有spec执行前只调用一次,而afterAll()
则是在所有spec结束后调用。这两个函数可以用来加速那些需要花费昂贵的初始化和销毁的测试单元。例如:
describe("A spec using beforeAll and afterAll", function() {
var foo;
beforeAll(function() {
foo = 1;
});
afterAll(function() {
foo = 0;
});
it("sets the initial value of foo before specs run", function() {
expect(foo).toEqual(1);
foo += 1;
});
it("does not reset foo between specs", function() {
expect(foo).toEqual(2);
});
});
代码调用beforeAll()
在所有测试spec开始前对foo赋值为1,在之后的测试改变了foo的值,但不再调用其初始化函数,在下一个spec内,foo等于2,。之后调用afterAll()
将foo赋值为0。
然而,要小心使用beforeAll()
和afterAll()
函数,毕竟他们在每个spec之间没有进行重置,很容易意外导致在你的测试体之间泄露状态,最终使他们产生错误或无法通过测试。
this
关键字
除了在describe()
声明变量外,另外一个在beforeEach()
,it()
,afterEach()
之间共享变量的方法是使用this关键字。每一个spec的beforeEach()
,it()
,afterEach()
拥有相同的在下个spec的beforeEach()
,it()
,afterEach()
执行之前会延迟置空的空对象this
。代码如下:
describe("A spec", function() {
beforeEach(function() {
this.foo = 0;
});
it("can use the `this` to share state", function() {
expect(this.foo).toEqual(0);
this.bar = "test pollution?";
});
it("prevents test pollution by having an empty `this` created for the next spec", function() {
expect(this.foo).toEqual(0);
expect(this.bar).toBe(undefined);
});
});
代码在每个spec之前声明this.foo
并赋值,在其it()
函数里能够共享该变量。而在it()
函数里声明的this.bar
,在下一个spec执行时无法获得,此时两者的this
关键字所指向的并不一致。
嵌套describe代码块
describe
可以嵌套调用,而spec可以在任何层次都可以定义。类似于组成一个Suites的树形结构。Spec执行前,Jasmine会按顺序沿着这个树形结构执行每个beforeEach()
函数。而执行结束后,Jasmine也将沿着树执行afterEach()
函数。例如:
describe("A spec", function() {
var foo;
beforeEach(function() {
foo = 0;
foo += 1;
});
afterEach(function() {
foo = 0;
});
it("is just a function, so it can contain any code", function() {
expect(foo).toEqual(1);
});
it("can have more than one expectation", function() {
expect(foo).toEqual(1);
expect(true).toEqual(true);
});
describe("nested inside a second describe", function() {
var bar;
beforeEach(function() {
bar = 1;
});
it("can reference both scopes as needed", function() {
expect(foo).toEqual(bar);
});
});
});
代码嵌套调用了describe()
函数,而上一层声明的变量,在下一层Suites仍可以使用。
禁用Suites
通过调用xdescribe()
函数可以禁用Suites。代码执行时,这些Suites以及里面的任何spec都将跳过,因而他们的结果也不会出现在最终的输出结果中。例如:
xdescribe("A spec", function() {
var foo;
beforeEach(function() {
foo = 0;
foo += 1;
});
it("is just a function, so it can contain any code", function() {
expect(foo).toEqual(1);
});
});
输出结果如图:
挂起Specs
被挂起的spec不会执行。但是他们的名字仍会显示在pending的结果集中。
声明挂起的方式有三种:
1. spec可以使用xit()
来声明挂起;
2. spec声明时不添加任何函数体也可以在结果中达到挂起的效果;
3. 如果你在spec体内的任何位置调用pending()函数,不管那个断言通过与否,该spec都将被挂起。当Suites结束后,pending所传递的字符串将被作为挂起的理由而显示。
例如:
describe("Pending specs", function() {
xit("can be declared 'xit'", function() {
//使用xit()函数挂起
expect(true).toBe(false);
});
it("can be declared with 'it' but without a function");
//使用空函数体将spec挂起
it("can be declared by calling 'pending' in the spec body", function() {
expect(true).toBe(false);
pending('this is why it is pending'); //调用pending()将其挂起
});
});
最终显示结果为:
有兴趣的同学可以查看下一篇文章:
Jasmine文档(二)