是这样的,我在用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的模块的时候,是不是要写成无状态的呢?
谢谢先。