是这样的,我在用express写一个Restful的app,然后其中有一个文件:
'use strict';
import lodash from 'lodash';
const parseActions = actions => {
return actions.map(e => {
const temp = e.split(' ');
return temp.length > 1 ? {
method: temp[0].trim(),
urlRegex: temp[1].trim()
} : {
urlRegex: temp[0].trim()
};
});
};
const authService = {
excludedAction: [],
setExcludedAction(actions){
this.excludedAction = parseActions(actions);
},
isExcludedAction(verb, url){
return lodash.some(this.excludedAction, function(e) {
const matchUrl = new RegExp('^' + e.urlRegex + '$').test(url);
return lodash.isEmpty(e.method) ? matchUrl : (e.method.toUpperCase() === verb.toUpperCase() && matchUrl)});
}
};
export default authService;
这个文件会返回一个叫’authService’的对象,其有一个属性是excludedAction
。这个对象在整个app中只有一个,在app运行的时候需要对其赋值:
const excluded = ['/api/login.*', '/api/posts.*', '/api/comments.*', '/api/users.*'];
authService.setExcludedAction(excluded);
而我在测试authService的时候,给excludedAction
赋了另一个值,下面是测试:
'use strict';
import should from 'should';
import authService from '../../src/auth/service';
describe('auth service', _ => {
let originalActions = [];
before(function(){
originalActions = authService.excludedAction;
authService.setExcludedAction([
'post /api/posts',
'get /api/posts/.+',
'/api/comments',
'/api/users.*'
]);
});
after(function(){
authService.excludedAction = originalActions;
});
it('should return true given excluded action', function() {
const result = authService.isExcludedAction('post', '/api/posts');
result.should.be.exactly(true);
});
it('should return true given excluded action which requires regex', function() {
const result = authService.isExcludedAction('get', '/api/posts/lkjdslfk123');
result.should.be.exactly(true) ;
});
it('should return false given not excluded url', function() {
const result = authService.isExcludedAction('get', '/api/posts');
result.should.be.exactly(false);
});
it('should return false given not excluded verb', function() {
const result = authService.isExcludedAction('delete', '/api/posts');
result.should.be.exactly(false);
});
it('should return true when there is no verb in configuration', function(){
const result = authService.isExcludedAction('get', '/api/comments');
result.should.be.exactly(true);
});
it('should exclude all users action', function() {
let result = authService.isExcludedAction('get', '/api/users');
result.should.be.exactly(true);
result = authService.isExcludedAction('get', '/api/users/ljsdlkf123');
result.should.be.exactly(true);
result = authService.isExcludedAction('put', '/api/users/ljsdlkf123');
result.should.be.exactly(true);
result = authService.isExcludedAction('post', '/api/users');
result.should.be.exactly(true);
result = authService.isExcludedAction('delete', '/api/users/lksjdfjk89');
result.should.be.exactly(true);
result = authService.isExcludedAction('POST', '/api/users/lksjdfjk89/posts');
result.should.be.exactly(true);
});
});
然后还有其他的测试,如果去掉测试里面的after
方法,那么在单独跑每一个测试文件的时候是过的,但是一起跑的时候就会有问题。后来发现原因是在authService.spec.js这个测试里面改变了excludedAction的值。而其他的测试用的是改变之后的值,这时候其他的测试就挂掉了。
这里的问题是authService在app里面是一个单例,同时它又有状态。这意味着每一个authService的使用者都只能对其状态进行读取,而无法进行修改,否则一旦一个使用者修改了而没有通知另一个使用者,那么程序就会出错。所以,我们在写类似authService的模块的时候,是不是要写成无状态的呢?
谢谢先。
无session就无状态
我觉得~不一定~
像 app = express() 然后 app.get(’/’, …) 就是给 app下面的一个默认的 router(app._router ? 记不清了) 来绑定了 /
的 handler, 也是改变了 app 的状态, 测试的时候可以每次去 new 一个 express app. 不一定要公用。
你可以导出 factory method, 然后去实例化 instance. 导出一个 single instance 是比较麻烦。
@i5ting 这里的状态,我觉得是object内部的属性。我从java转过来,像spring里面的service一般都是无状态的,它会被实例化为单例。
@magicdawn 如果是有多个instance的话,那我觉得使用ES6的class比较容易些,导出类,然后new出一个instance。
@kolyjjj whatever , app 是有状态的,所以别怕改变。
@magicdawn 以前在java里面,倾向于无状态的原因是在多线程的情况下,多个线程对同一个状态的改变,搞起来比较复杂。如果没有状态,就比较简单了。不过由于nodejs是单线程的,所以应该不用考虑状态被多个线程改动的情况。