提问人:Mogsdad 提问时间:4/19/2013 最后编辑:TheMasterMogsdad 更新时间:10/25/2021 访问量:40115
如何在 GAS 中测试触发函数?
How can I test a trigger function in GAS?
问:
Google Apps 脚本支持触发器,用于将事件传递给触发器函数。遗憾的是,开发环境将允许您在不传递参数的情况下测试函数,因此您无法以这种方式模拟事件。如果尝试,则会出现如下错误:
ReferenceError: 'e' 未定义。
或
TypeError:无法从未定义中读取属性 *...*
(其中未定义)e
可以将事件视为可选参数,并使用 Is there a better way to do optional function parameters in JavaScript?中的任何技术将默认值插入触发器函数中。但这会带来一个风险,即一个懒惰的程序员(如果是你的话,请举手!)会留下这些代码,并产生意想不到的副作用。
肯定有更好的方法吗?
答:
您可以编写一个测试函数,将模拟事件传递给触发器函数。下面是一个测试触发器函数的示例。它传递一个事件对象,其中包含了解事件中为“电子表格编辑事件”描述的所有信息。onEdit()
要使用它,请在目标函数中设置断点,选择函数并点击 。onEdit
test_onEdit
Debug
/**
* Test function for onEdit. Passes an event object to simulate an edit to
* a cell in a spreadsheet.
*
* Check for updates: https://stackoverflow.com/a/16089067/1677912
*
* See https://developers.google.com/apps-script/guides/triggers/events#google_sheets_events
*/
function test_onEdit() {
onEdit({
user : Session.getActiveUser().getEmail(),
source : SpreadsheetApp.getActiveSpreadsheet(),
range : SpreadsheetApp.getActiveSpreadsheet().getActiveCell(),
value : SpreadsheetApp.getActiveSpreadsheet().getActiveCell().getValue(),
authMode : "LIMITED"
});
}
如果您好奇,这是为了测试以三个单元格为条件的 Google 电子表格的函数而编写的。onEdit
下面是电子表格表单提交事件的测试函数。它通过读取表单提交数据来构建其模拟事件。这最初是为在 onFormSubmit 触发器中获取 TypeError 而编写的。
/**
* Test function for Spreadsheet Form Submit trigger functions.
* Loops through content of sheet, creating simulated Form Submit Events.
*
* Check for updates: https://stackoverflow.com/a/16089067/1677912
*
* See https://developers.google.com/apps-script/guides/triggers/events#google_sheets_events
*/
function test_onFormSubmit() {
var dataRange = SpreadsheetApp.getActiveSheet().getDataRange();
var data = dataRange.getValues();
var headers = data[0];
// Start at row 1, skipping headers in row 0
for (var row=1; row < data.length; row++) {
var e = {};
e.values = data[row].filter(Boolean); // filter: https://stackoverflow.com/a/19888749
e.range = dataRange.offset(row,0,1,data[0].length);
e.namedValues = {};
// Loop through headers to create namedValues object
// NOTE: all namedValues are arrays.
for (var col=0; col<headers.length; col++) {
e.namedValues[headers[col]] = [data[row][col]];
}
// Pass the simulated event to onFormSubmit
onFormSubmit(e);
}
}
技巧
模拟事件时,请注意尽可能接近记录的事件对象。
如果要验证文档,可以记录从触发器函数接收到的事件。
Logger.log( JSON.stringify( e , null, 2 ) );
在电子表格表单提交事件中:
- 所有 namedValues 值都是数组。
- 时间戳是字符串,其格式将本地化为窗体的区域设置。如果从具有默认格式*的电子表格中读取,则它们是 Date 对象。如果触发器函数依赖于时间戳的字符串格式(这是一个坏主意),请注意确保适当地模拟该值。
- 如果电子表格中的列不在表单中,则此脚本中的技术将模拟包含这些附加值的“事件”,这不是您从表单提交中收到的内容。
- 如问题 4335 中所述,数组跳过空白答案(在“新表单”+“新工作表”中)。该方法用于模拟此行为。
values
filter(Boolean)
*格式为“纯文本”的单元格会将日期保留为字符串,这不是一个好主意。
评论
2017年更新:使用 Google Apps 脚本的 Stackdriver 日志记录调试事件对象。在脚本编辑器的菜单栏中,转到:查看或流式传输日志。View > Stackdriver Logging
console.log() 将写入级别消息DEBUG
示例 onEdit():
function onEdit (e) {
var debug_e = {
authMode: e.authMode,
range: e.range.getA1Notation(),
source: e.source.getId(),
user: e.user,
value: e.value,
oldValue: e. oldValue
}
console.log({message: 'onEdit() Event Object', eventObject: debug_e});
}
示例 onFormSubmit():
function onFormSubmit (e) {
var debug_e = {
authMode: e.authMode,
namedValues: e.namedValues,
range: e.range.getA1Notation(),
value: e.value
}
console.log({message: 'onFormSubmit() Event Object', eventObject: debug_e});
}
示例 onChange():
function onChange (e) {
var debug_e = {
authMode: e.authMode,
changeType: changeType,
user: e.user
}
console.log({message: 'onChange() Event Object', eventObject: debug_e});
}
然后检查 Stackdriver UI 中标记为字符串的日志以查看输出message
2020-2021年更新:
您不需要使用前面答案中建议的任何类型的模拟事件。
如问题中所述,如果直接在脚本编辑器中“运行”该函数,则会出现类似
TypeError:无法从未定义中读取属性...
被抛出。这些不是真正的错误。此错误只是因为您在没有事件的情况下运行了该函数。如果你的函数没有按预期运行,你需要找出实际的错误:
要测试触发函数,
手动触发相应的事件:即,要测试,编辑工作表中的单元格;要进行测试,请提交虚拟表单响应;若要进行测试,请将浏览器导航到已发布的 Web 应用 URL。
onEdit
onFormSubmit
doGet
/exec
如果有任何错误,则会记录到 stackdriver 中。要查看这些日志,
在脚本编辑器中>左侧栏上的“执行”图标(旧版编辑器:查看>执行)。
或者,单击此处>单击您感兴趣的项目>单击左侧栏上的“执行”图标(第 4 个)
您可以在执行页面中找到执行列表。确保清除所有过滤器,例如左上角的“Ran as:Me”以显示所有执行。单击您感兴趣的执行,它将以红色显示导致触发器失败的错误。
注意:有时,由于错误,日志不可见。尤其是在 Web 应用程序由匿名用户运行的情况下,情况尤其如此。在这种情况下,建议将默认 Google 云项目切换到标准 Google 云项目,并直接使用 View> Stackdriver 日志记录。有关详细信息,请参阅此处。
- 若要进一步调试,可以使用编辑代码添加到感兴趣的任何行之后,以查看该对象的详细信息。强烈建议您将要查找的对象字符串化:因为日志查看器具有特殊性。添加后,从步骤 1 开始重复。重复此循环,直到缩小问题范围。
console.log(/*object you're interested in*/)
console.log(JSON.stringify(e))
console.log()
恭喜!你已经成功地解决了问题并越过了第一个障碍。
评论
作为上述方法(2020 年更新)第 4 点的补充: 这是我用来跟踪触发代码的一个小例程,它已经为我节省了很多时间。此外,我打开了两个窗口:一个是堆栈驱动程序(执行),另一个是代码(主要驻留在库中),因此我可以轻松找到罪魁祸首。
/**
*
* like Logger.log %s in text is replaced by subsequent (stringified) elements in array A
* @param {string | object} text %s in text is replaced by elements of A[], if text is not a string, it is stringified and A is ignored
* @param {object[]} A array of objects to insert in text, replaces %s
* @returns {string} text with objects from A inserted
*/
function Stringify(text, A) {
var i = 0 ;
return (typeof text == 'string') ?
text.replace(
/%s/g,
function(m) {
if( i >= A.length) return m ;
var a = A[i++] ;
return (typeof a == 'string') ? a : JSON.stringify(a) ;
} )
: (typeof text == 'object') ? JSON.stringify(text) : text ;
}
/* use Logger (or console) to display text and variables. */
function T(text) {
Logger.log.apply(Logger, arguments) ;
var Content = Stringify( text, Array.prototype.slice.call(arguments,1) ) ;
return Content ;
}
/**** EXAMPLE OF USE ***/
function onSubmitForm(e) {
T("responses:\n%s" , e.response.getItemResponses().map(r => r.getResponse()) ;
}
评论