node-testing

更新中...

Mocha

Mocha is a simple, flexible, fun JavaScript test framework for node.js and the browser. For more information view the documentation.

Intro

搭建一个测试原型

1
2
//f2e 是原型生成的目录
mocha init f2e

浏览器测试 饭桶js

npm i -g mocha-phantomjs

npm i 为npm install的缩写

1
if (window.mochaPhantomJS) { mochaPhantomJS.run(); }

出于应用健壮性的考量,针对前端 js 脚本的单元测试也非常重要。而前后端通吃,也是 mocha 的一大特点。

场景 Situation

  • 在我们现在的subcase项目中,统一了ret_code作为成功与否的标志,方便测试。
  • HTTP Status testing Send HTTP requests, receive responses and check HTTP statuses

Example

Simple example

1
2
3
4
5
6
7
8
9
var should = require('chai').should(),
scapegoat = require('../index'),
escape = scapegoat.escape,
unescape = scapegoat.unescape;
describe('#escape', function() {
it('converts & into &', function() {
escape('&').should.equal('&');
});

API Testing Example

from https://gist.github.com/vgheri/5430387#file-test-js

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
describe('Account', function() {
it('should return error trying to save duplicate username', function(done) {
var profile = {
username: 'vgheri',
password: 'test',
firstName: 'Valerio',
lastName: 'Gheri'
};
// once we have specified the info we want to send to the server via POST verb,
// we need to actually perform the action on the resource, in this case we want to
// POST on /api/profiles and we want to send some info
// We do this using the request object, requiring supertest!
request(url)
.post('/api/profiles')
.send(profile)
// end handles the response
.end(function(err, res) {
if (err) {
throw err;
}
// this is should.js syntax, very clear
res.should.have.status(400);
done();
});
});
it('should correctly update an existing account', function(done){
var body = {
firstName: 'JP',
lastName: 'Berd'
};
request(url)
.put('/api/profiles/vgheri')
.send(body)
.expect('Content-Type', /json/)
.expect(200) //Status code
.end(function(err,res) {
if (err) {
throw err;
}
// Should.js fluent syntax applied
res.body.should.have.property('_id');
res.body.firstName.should.equal('JP');
res.body.lastName.should.equal('Berd');
res.body.creationDate.should.not.equal(null);
done();
});
});
});
});
1
2
3
4
5
6
7
8
9
describe('#index', function () {
it('should get /topic/:tid 200', function (done) {
request.get('/topic/' + support.testTopic._id)
.expect(200, function (err, res) {
res.text.should.containEql('test topic content');
res.text.should.containEql('alsotang');
done(err);
});
});

涉及到插入的时候,之前写rspec也遇到类似的
的情况,可以通过测试count是否增加1。

Fun

mocha -R nyan and whole lots of other stuff.

1
2
3
4
5
6
1 -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-__,------,
0 -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-__| /\_/\
0 -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_~|_( ^ .^)
-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ "" ""
1 passing (141ms)

Chai

Chai is a BDD / TDD assertion library for node and the browser that can be delightfully paired with any javascript testing framework.

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
Should
chai.should();
foo.should.be.a('string');
foo.should.equal('bar');
foo.should.have.length(3);
tea.should.have.property('flavors')
.with.length(3);
Expect
var expect = chai.expect;
expect(foo).to.be.a('string');
expect(foo).to.equal('bar');
expect(foo).to.have.length(3);
expect(tea).to.have.property('flavors')
.with.length(3);
Assert
var assert = chai.assert;
assert.typeOf(foo, 'string');
assert.equal(foo, 'bar');
assert.lengthOf(foo, 3)
assert.property(tea, 'flavors');
assert.lengthOf(tea.flavors, 3);

SuperTest

Miscs

Be kind and don’t make developers hunt around in your docs to figure out how to run the tests, add a make test target to your Makefile - TJ

Nock

Nock is an HTTP mocking library that can record HTTP calls and play them back, so that you can either mock an API by hand, or just record actual HTTP calls and use their responses in future test runs. Our orchestrate.js library does the former, but I’ll show you how to do the latter.

转自链接

《九浅一深node.js》Book Note

更新中。。。

模块机制

  • CommonJS希望JS可以在任何地方运行。
  • exports是唯一导出的接口,在模块中有一个module对象,代表自己,而exports就是module的属性。
  • require允许不包含扩展名,会按照.js, .node, .json尝试。
  • 同步配合缓存,可以大幅度缓解Node单线程阻塞调用的缺陷。
  • 18 ~

异步I/O

  • I/O disctributed I/O is pretty expensive
  • The cost of multi-thread when you create thread, and switch between them is expensive.
  • Asynchronous I/O
1
2
3
4
5
6
7
8
9
App OS
I/O call------> process request
/||
other call ||
||
||
||/
callback process done
  • repeatly to call I/O is 轮询
  • 55 ~

Asynchronous Programming

Continuation Passing Style

1
2
3
function foo(x, bar) {
return bar(x);
}

业务重点由返回值转移到了毁掉函数中。

  • 偏函数
1
2
3
4
5
6
7
8
9
10
var toString = Object.prototype.toString;
var isString = function(obj) {
return toString.call(obj) == '[object string]';
};
var isType = function(type) {
return function(obj) {
return toString.call(obj) == '[object' + type + ']';
};
};
  • 雪崩就是高访问并发的时候大量请求同时涌入数据库。
    用事件队列解决
1
2
3
4
5
6
7
8
9
10
11
12
13
var proxy = new events.EventEmitter();
var status = "ready";
var select = function(callback) {
proxy.once("selected", callback);
if (status == "ready") {
status = "pending";
db.select("SQL", function(results) {
proxy.emits("selected", results);
status = "ready";
});
}
};
// 保证相同的SQL语句从开始到结束永远只有一次,Node单线程执行所以无需担心状态同步问题。
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
var count = 0;
var results = {};
var done = unction(key, value) {
results[key] = value;
count++;
if (count === 3) {
render(results);
}
};
fs.readFile(template_path, "utf8", function(err, template) {
done("template", template);
});
db.query(sql, function(err, data) {
done("data", data);
});
/* L10n.get
Synchronous method for retrieving localization strings from app's resources. This function should
be used only when direct manipulation on the strings is needed (etc. date/time formatting) and in
all other UI related cases should be replaced with DOM attributes setting or L10n.setAttributes().
This method has also potential to generate race conditions so a developer has to make sure that any
code that uses this method is not fired before l10n resources are loaded using L10n.once() or L10n.
ready().*/
l10.get(function(err, resources) {
done("resources", resources);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
// 烧饼(哨兵)变量和偏函数结合
var after = function(times, callback) {
var count = 0, resutls = {};
return function(key, value) {
results[key] = value;
counts++;
if (count === times) {
callback(results);
}
};
};
var done = after(times, render);
1
2
3
4
5
6
7
8
9
10
// 多对一的收敛和事件订阅/发布
var emitter = new events.Emitter();
var done = after(times, render);
emitter.on("done", done);
emitter.on("done", other);
fs.readFile(template_path, "utf8", function(err, template) {
emitter.emit("done", "template", template);
})
  • 74 ~

构建Web应用