chai

概念

测试技术的断言框架。

特点

  • 支持多种BDD/TDD断言语法
    • BDD:should
    • BDD:expect
    • TDD:assert
  • 可用在browser端和node端。
  • 可以和很多测试框架结合例如mocha jasmine等进行单元和UI测试。

安装

1
npm install chai

用法

browser端

1
2
3
4
5
<script src="//cdn.bootcss.com/chai/3.4.0/chai.js"></script>
<script>
//expect为全局的函数
expect(foo).to.not.equal('bar');
</script>

node端

1
2
3
var chai = require('chai'),
expect = chai.expect;
chai.should();

expect用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
//------------------ 连接词用法 -----------------
//not用法 expect().not.to.
expect(foo).to.not.equal('bar');
expect(goodFn).to.not.throw(Error);
expect({ foo: 'baz' }).to.have.property('foo').and.not.equal('bar');
//deep用法 expect(foo).to.deep. 通常和equal连用,判断object的相等需要用deep
expect(foo).to.deep.equal({ bar: 'baz' });
//any用法 用在keys的判断上
expect({ foo: 1, bar: 2 }).to.have.any.keys('foo', 'baz');
// all用法 用在keys的判断上
expect(foo).to.have.all.keys('bar', 'baz');
expect({ foo: 1, bar: 2 }).to.have.all.keys(['bar', 'foo']);
//a 判断typeof 或者 language chain
// typeof
expect('test').to.be.a('string');
expect({ foo: 'bar' }).to.be.an('object');
expect(null).to.be.a('null');
expect(undefined).to.be.an('undefined');
// language chain
expect(foo).to.be.an.instanceof(Foo);
//---------------------判断bool----------------------
//bool
// 1 truthy
expect('everthing').to.be.ok;
expect(1).to.be.ok;
expect(false).to.not.be.ok;
expect(undefined).to.not.be.ok;
expect(null).to.not.be.ok;
//2 true
expect(true).to.be.true;
expect(1).to.not.be.true;
//3 false
expect(false).to.be.false;
expect(0).to.not.be.false;
//4 null
expect(null).to.be.null;
expect(undefined).not.to.be.null;
// 5 undefined
expect(undefined).to.be.undefined;
expect(null).to.not.be.undefined;
//6 exist
var foo = 'hi'
, bar = null
, baz;
expect(foo).to.exist;
expect(bar).to.not.exist;
expect(baz).to.not.exist;
//7 expty
expect([]).to.be.empty;
expect('').to.be.empty;
expect({}).to.be.empty;
//------------------------判断函数参数---------------------------
// arguments
function test () {
expect(arguments).to.be.arguments;
}
//------------------------判断相等和大小关系--------------------------------
// equal if the deep flag is set,
// attention: asserts that the target is deeply equal to value.
expect('hello').to.equal('hello');
expect(42).to.equal(42);
expect(1).to.not.equal(true);
expect({ foo: 'bar' }).to.not.equal({ foo: 'bar' });
expect({ foo: 'bar' }).to.deep.equal({ foo: 'bar' });
// eql: 判断值等
expect({ foo: 'bar' }).to.eql({ foo: 'bar' });
expect([ 1, 2, 3 ]).to.eql([ 1, 2, 3 ]);
//.above:大于
expect(10).to.be.above(5);
expect('foo').to.have.length.above(2);
expect([ 1, 2, 3 ]).to.have.length.above(2);
//least 至少
expect('foo').to.have.length.of.at.least(2);
expect([ 1, 2, 3 ]).to.have.length.of.at.least(3);
//below 低于
expect(5).to.be.below(10);
expect('foo').to.have.length.below(4);
expect([ 1, 2, 3 ]).to.have.length.below(4);
//most 最大为
expect(5).to.be.at.most(5);
expect('foo').to.have.length.of.at.most(4);
expect([ 1, 2, 3 ]).to.have.length.of.at.most(3);
//.within(start, finish)在什么区间内
expect(7).to.be.within(5,10);
expect('foo').to.have.length.within(2,4);
expect([ 1, 2, 3 ]).to.have.length.within(2,4);
//.closeTo(expected, delta)
expect(1.5).to.be.closeTo(1, 0.5);
//------------------正则---------------
//match(regexp)
expect('foobar').to.match(/^foo/);
//-----------------字符串-------------
//string 判断含有某字符串
expect('foobar').to.have.string('bar');
//----------------throw---------------
var err = new ReferenceError('This is a bad function.');
var fn = function () { throw err; }
expect(fn).to.throw(ReferenceError);
expect(fn).to.throw(Error);
expect(fn).to.throw(/bad function/);
expect(fn).to.not.throw('good function');
expect(fn).to.throw(ReferenceError, /bad function/);
expect(fn).to.throw(err);
expect(fn).to.not.throw(new RangeError('Out of range.'));
//------------------------object相关判断-------------------------
//deep & property属性
expect(foo).to.deep.equal({ bar: 'baz' });
expect({ foo: { bar: { baz: 'quux' } } }).to.have.deep.property('foo.bar.baz', 'quux');
// typeof
expect('test').to.be.a('string');
expect({ foo: 'bar' }).to.be.an('object');
expect(null).to.be.a('null');
expect(undefined).to.be.an('undefined');
// language chain
expect(foo).to.be.an.instanceof(Foo);
// include
expect([1,2,3]).to.include(2);
expect('foobar').to.contain('foo');
expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo');
// members 判断数组成员
expect([1, 2, 3]).to.include.members([3, 2]);
expect([1, 2, 3]).to.not.include.members([3, 2, 8]);
expect([4, 2]).to.have.members([2, 4]);
expect([5, 2]).to.not.have.members([5, 2, 1]);
expect([{ id: 1 }]).to.deep.include.members([{ id: 1 }]);
//respondTo(method) 判断是否是原型方法
Klass.prototype.bar = function(){};
expect(Klass).to.respondTo('bar');
expect(obj).to.respondTo('bar');
Klass.baz = function(){};
expect(Klass).itself.to.respondTo('baz');
//itself和respondTo结合起来判断是否是原型链的方法还是自身的方法
function Foo() {}
Foo.bar = function() {}
Foo.prototype.baz = function() {}
expect(Foo).itself.to.respondTo('bar');
expect(Foo).itself.not.to.respondTo('baz');
//change 判断函数是否改变了对象的属性值
var obj = { val: 10 };
var fn = function() { obj.val += 3 };
var noChangeFn = function() { return 'foo' + 'bar'; }
expect(fn).to.change(obj, 'val');
expect(noChangFn).to.not.change(obj, 'val')
//increase(function) 函数是否升高了属性值
var obj = { val: 10 };
var fn = function() { obj.val = 15 };
expect(fn).to.increase(obj, 'val');
//.decrease(function) 函数是否降低了属性值
var obj = { val: 10 };
var fn = function() { obj.val = 5 };
expect(fn).to.decrease(obj, 'val');
//keys.判断是否object含有某项属性
//Note, either any or all should be used in the assertion. If neither are used, the assertion is defaulted to all.
expect({ foo: 1, bar: 2 }).to.have.any.keys('foo', 'baz');
expect({ foo: 1, bar: 2 }).to.have.any.keys('foo');
expect({ foo: 1, bar: 2 }).to.contain.any.keys('bar', 'baz');
expect({ foo: 1, bar: 2 }).to.contain.any.keys(['foo']);
expect({ foo: 1, bar: 2 }).to.contain.any.keys({'foo': 6});
expect({ foo: 1, bar: 2 }).to.have.all.keys(['bar', 'foo']);
expect({ foo: 1, bar: 2 }).to.have.all.keys({'bar': 6, 'foo', 7});
expect({ foo: 1, bar: 2, baz: 3 }).to.contain.all.keys(['bar', 'foo']);
expect({ foo: 1, bar: 2, baz: 3 }).to.contain.all.keys([{'bar': 6}}]);

should用法

同chai的差别详情参考

1
2
3
4
5
6
var chai = require('chai');
chai.should();
//语法: 基本是 expect().to.xx 相当于 ().should.xx ****
foo.should.be.a('string'); //expect(foo).to.be.a('string');
foo.should.equal('bar'); //expect(foo).to.equal('bar');
//省略用法,见expect

注意:should在IE9下有问题

assert

assert为TDD用法,现在一般都是用基于BDD的测试,所以省略,详情请参考 Assert

chai as promise用法

  • 将promise和chai结合起来,用于在某种异步的条件下形成的断言判断
  • attention: Chai as Promised is only compatible with modern browsers (IE ≥9, Safari ≥6, no PhantomJS)
  • 具体用法:参见
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
doSomethingAsync().then(
function (result) {
result.should.equal("foo");
done();
},
function (err) {
done(err);
}
);
//安装: npm install chai-as-promised
//引用chai as promise后可以写作 should.eventually.xxx
var chai = require("chai");
var chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
var should = chai.should();
return doSomethingAsync().should.eventually.equal("foo");
//在ui测试中可以写作
return driver.getAttribute(input, 'type').should.eventually.equal(fieldModel.type);
return promise.should.be.fulfilled;
return promise.should.eventually.deep.equal("foo");
return promise.should.become("foo"); // same as `.eventually.deep.equal`
return promise.should.be.rejected;
return promise.should.be.rejectedWith(Error); // other variants of Chai's `throw` assertion work too.
// 通过覆盖chaiAsPromised.transferPromiseness方法将assertion赋予then的链式调用功能
// 应用例子 wd.js中 chaiAsPromised.transferPromiseness = wd.transferPromiseness;
chaiAsPromised.transferPromiseness = function (assertion, promise) {
assertion.then = promise.then.bind(promise); // this is all you get by default
assertion.finally = promise.finally.bind(promise);
assertion.done = promise.done.bind(promise);
};

sinon-chai用法

  • sinon-chai 用于对Sinon.JS中的spy, stub, and mocking framework进行断言
  • 具体用法,参见
  • API为:
Sinon.JS property/method Sinon–Chai assertion
called spy.should.have.been.called
callCount spy.should.have.callCount(n)
calledOnce spy.should.have.been.calledOnce
calledTwice spy.should.have.been.calledTwice
calledThrice spy.should.have.been.calledThrice
calledBefore spy1.should.have.been.calledBefore(spy2)
calledAfter spy1.should.have.been.calledAfter(spy2)
calledWithNew spy.should.have.been.calledWithNew
alwaysCalledWithNew spy.should.always.have.been.calledWithNew
calledOn spy.should.have.been.calledOn(context)
alwaysCalledOn spy.should.always.have.been.calledOn(context)
calledWith spy.should.have.been.calledWith(…args)
alwaysCalledWith spy.should.always.have.been.calledWith(…args)
calledWithExactly spy.should.have.been.calledWithExactly(…args)
alwaysCalledWithExactly spy.should.always.have.been.calledWithExactly(…args)
calledWithMatch spy.should.have.been.calledWithMatch(…args)
alwaysCalledWithMatch spy.should.always.have.been.calledWithMatch(…args)
returned spy.should.have.returned(returnVal)
alwaysReturned spy.should.have.always.returned(returnVal)
threw spy.should.have.thrown(errorObjOrErrorTypeStringOrNothing)
alwaysThrew spy.should.have.always.thrown(errorObjOrErrorTypeStringOrNothing)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//安装 npm install sinon-chai
//用法
var chai = require("chai");
var sinonChai = require("sinon-chai");
chai.should();
chai.use(sinonChai);
function hello(name, cb) {
cb("hello " + name);
}
describe("hello", function () {
it("should call callback with correct greeting", function () {
var cb = sinon.spy();
hello("foo", cb);
cb.should.have.been.calledWith("hello foo");
//if expect expect(cb).to.have.been.calledWith("hello foo");
});
});

chai和mocha结合的测试用例