QUnit 测试依赖文档属性的函数
QUnit testing a function which leans on document properties
我正在尝试为专为 Google Docs 设计的 Apps 脚本插件编写一些单元测试。一些函数我想对调用 PropertiesService.getDocumentProperties()
进行单元测试。我的附加组件中的一个简单示例函数:
function baseFontSize() {
var baseFontSize = JSON.parse(
PropertiesService.getDocumentProperties().getProperty('baseFontSize'));
if (baseFontSize === null) {
baseFontSize = JSON.parse(
PropertiesService.getUserProperties().getProperty('baseFontSize'));
if (baseFontSize === null) {
PropertiesService.getUserProperties().setProperty('baseFontSize', '11');
baseFontSize = 11;
}
PropertiesService.getDocumentProperties()
.setProperty('baseFontSize', JSON.stringify(baseFontSize));
}
return baseFontSize;
}
我正在使用 QUnit 为 Google Apps 脚本库编写测试:
function doGet(e) {
QUnit.urlParams(e.parameter);
QUnit.config({title: 'My test suite'});
QUnit.load(testSuite);
return QUnit.getHtml();
}
QUnit.helpers(this);
function testSuite() {
// some module() and test() calls deleted for brevity...
test('baseFontSize', function() {
// PropertiesService.getDocumentProperties() === null
// how to test baseFontSize()?
});
// more module() and test() calls...
}
由于测试套件不在文档中 运行,因此没有文档属性。似乎测试我的函数的唯一方法是模拟 getDocumentProperties
函数。当然,我能找到的唯一 Apps 脚本 mock/stub 库要么是为了在 Node.js 环境中进行测试,要么是不够完整,无法满足我的需求,这意味着我必须自己动手。
虽然我仍然希望找到一个更优雅的解决方案,但在此之前我已经拼凑了一个基于 SinonJS API 的简单存根框架,并将存根注入到我要测试的函数中(因为我实际上不能存根 PropertiesService
,因为它被冻结了)。
大致来说,我的解决方案(我的项目已经包含 Underscore,所以我尽可能地利用它):
function stub(object, property, impl) {
const original = object[property];
if (_.isFunction(impl)) object[property] = wrapFunction(impl);
else object[property] = wrapFunction(function() { return impl; });
object[property].restore = function() {
if (!_.isUndefined(original)) object[property] = original;
else delete object[property];
};
return object[property];
}
function wrapFunction(fn) {
const properties = _.mapObject({
get callCount() { return calls.length; },
get called() { return calls.length > 0; },
get notCalled() { return calls.length === 0; },
// etc...
}, function(v, k, o) { return Object.getOwnPropertyDescriptor(o, k); });
const calls = [];
const wrappedFn = function() {
const args = Array.prototype.slice.call(arguments);
var error;
var returnVal;
try { returnVal = fn.apply(this, args); }
catch (e) { error = e; }
calls.push({
thisObj: this,
params: args,
error: error,
returnVal: returnVal,
});
return returnVal;
};
Object.defineProperties(wrappedFn, properties);
return wrappedFn;
}
然后,我的测试:
test('baseFontSize', function() {
const propService = {};
stub(propService, 'getDocumentProperties', fakeGetProperties());
stub(propService, 'getUserProperties', fakeGetProperties());
equal(baseFontSize(propService), 11);
});
//...
function fakeGetProperties() {
const map = {};
const container = {
deleteAllProperties: function() {_.each(map, function(v, k){depete map[k];});},
deleteProperty: function(k) { delete map[k]; },
getKeys: function() { return Object.keys(map); },
getProperties: function() { return _.clone(map); },
getProperty: function(key) { return map[key] || null; },
setProperties: function(obj, deleteOthers) {
if (deleteOthers) container.deleteAllProerties();
_.each(obj, function(v, k) { map[k] = v; });
},
setProperty: function(key, value) { map[key] = value; },
};
return function() { return container; };
}
以及我要测试的函数的编辑版本,使用 DI for PropertiesService:
function baseFontSize(propService) {
if (_.isUndefined(propService)) {
propService = PropertiesService;
}
var baseFontSize = JSON.parse(
propService.getDocumentProperties().getProperty('baseFontSize'));
if (baseFontSize === null) {
baseFontSize = JSON.parse(
propService.getUserProperties().getProperty('baseFontSize'));
if (baseFontSize === null) {
propService.getUserProperties().setProperty('baseFontSize', '11');
baseFontSize = 11;
}
propService.getDocumentProperties()
.setProperty('baseFontSize', JSON.stringify(baseFontSize));
}
return baseFontSize;
}
我正在尝试为专为 Google Docs 设计的 Apps 脚本插件编写一些单元测试。一些函数我想对调用 PropertiesService.getDocumentProperties()
进行单元测试。我的附加组件中的一个简单示例函数:
function baseFontSize() {
var baseFontSize = JSON.parse(
PropertiesService.getDocumentProperties().getProperty('baseFontSize'));
if (baseFontSize === null) {
baseFontSize = JSON.parse(
PropertiesService.getUserProperties().getProperty('baseFontSize'));
if (baseFontSize === null) {
PropertiesService.getUserProperties().setProperty('baseFontSize', '11');
baseFontSize = 11;
}
PropertiesService.getDocumentProperties()
.setProperty('baseFontSize', JSON.stringify(baseFontSize));
}
return baseFontSize;
}
我正在使用 QUnit 为 Google Apps 脚本库编写测试:
function doGet(e) {
QUnit.urlParams(e.parameter);
QUnit.config({title: 'My test suite'});
QUnit.load(testSuite);
return QUnit.getHtml();
}
QUnit.helpers(this);
function testSuite() {
// some module() and test() calls deleted for brevity...
test('baseFontSize', function() {
// PropertiesService.getDocumentProperties() === null
// how to test baseFontSize()?
});
// more module() and test() calls...
}
由于测试套件不在文档中 运行,因此没有文档属性。似乎测试我的函数的唯一方法是模拟 getDocumentProperties
函数。当然,我能找到的唯一 Apps 脚本 mock/stub 库要么是为了在 Node.js 环境中进行测试,要么是不够完整,无法满足我的需求,这意味着我必须自己动手。
虽然我仍然希望找到一个更优雅的解决方案,但在此之前我已经拼凑了一个基于 SinonJS API 的简单存根框架,并将存根注入到我要测试的函数中(因为我实际上不能存根 PropertiesService
,因为它被冻结了)。
大致来说,我的解决方案(我的项目已经包含 Underscore,所以我尽可能地利用它):
function stub(object, property, impl) {
const original = object[property];
if (_.isFunction(impl)) object[property] = wrapFunction(impl);
else object[property] = wrapFunction(function() { return impl; });
object[property].restore = function() {
if (!_.isUndefined(original)) object[property] = original;
else delete object[property];
};
return object[property];
}
function wrapFunction(fn) {
const properties = _.mapObject({
get callCount() { return calls.length; },
get called() { return calls.length > 0; },
get notCalled() { return calls.length === 0; },
// etc...
}, function(v, k, o) { return Object.getOwnPropertyDescriptor(o, k); });
const calls = [];
const wrappedFn = function() {
const args = Array.prototype.slice.call(arguments);
var error;
var returnVal;
try { returnVal = fn.apply(this, args); }
catch (e) { error = e; }
calls.push({
thisObj: this,
params: args,
error: error,
returnVal: returnVal,
});
return returnVal;
};
Object.defineProperties(wrappedFn, properties);
return wrappedFn;
}
然后,我的测试:
test('baseFontSize', function() {
const propService = {};
stub(propService, 'getDocumentProperties', fakeGetProperties());
stub(propService, 'getUserProperties', fakeGetProperties());
equal(baseFontSize(propService), 11);
});
//...
function fakeGetProperties() {
const map = {};
const container = {
deleteAllProperties: function() {_.each(map, function(v, k){depete map[k];});},
deleteProperty: function(k) { delete map[k]; },
getKeys: function() { return Object.keys(map); },
getProperties: function() { return _.clone(map); },
getProperty: function(key) { return map[key] || null; },
setProperties: function(obj, deleteOthers) {
if (deleteOthers) container.deleteAllProerties();
_.each(obj, function(v, k) { map[k] = v; });
},
setProperty: function(key, value) { map[key] = value; },
};
return function() { return container; };
}
以及我要测试的函数的编辑版本,使用 DI for PropertiesService:
function baseFontSize(propService) {
if (_.isUndefined(propService)) {
propService = PropertiesService;
}
var baseFontSize = JSON.parse(
propService.getDocumentProperties().getProperty('baseFontSize'));
if (baseFontSize === null) {
baseFontSize = JSON.parse(
propService.getUserProperties().getProperty('baseFontSize'));
if (baseFontSize === null) {
propService.getUserProperties().setProperty('baseFontSize', '11');
baseFontSize = 11;
}
propService.getDocumentProperties()
.setProperty('baseFontSize', JSON.stringify(baseFontSize));
}
return baseFontSize;
}