// Apps Script

Test an onEdit or onFormSubmit trigger without waiting for the event.

Trigger functions receive an event object you can't produce by hand. Build a fake e with the exact fields your handler reads, then run it directly from the editor or a test function.

I want to run my onEdit or onFormSubmit handler right now, without actually editing a cell or submitting a form, so I can see whether the logic works.

The script

copy · paste · trigger
testTrigger.gs
Apps Script
// Fake the event object onEdit receives, then call the handler directly.
// Run testOnEdit() from the Apps Script editor to iterate without touching the sheet.
function testOnEdit() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName('Responses');

  // Mimic the event object Apps Script passes to onEdit(e)
  var fakeEvent = {
    source: ss,
    range: sheet.getRange(2, 3),   // row 2, col 3 — the cell you edited
    value: 'Approved',             // new cell value after the edit
    oldValue: 'Pending',           // value before the edit
    user: Session.getActiveUser()
  };

  onEdit(fakeEvent);
}

Need a variant? Gnaw writes a custom version from one sentence — fields, triggers, edge cases handled.

Walkthrough

Why the editor's Run button fails for trigger functions

When Apps Script fires onEdit, it constructs an event object and passes it as the first argument. That object carries e.range, e.value, e.oldValue, e.source, and e.user. When you hit Run in the editor, Apps Script calls the function with no arguments, so e is undefined. The first time you try e.range.getRow() inside an onEdit handler, you get a TypeError on a function that works perfectly once a real edit fires.

The fix is not to add null-guards everywhere. The fix is to build a fake event that matches the shape the real trigger would send, then call your handler with it. You stay in full control of which row, which column, and which value the test uses.

Fields to include in the mock event

The event schema is documented but easy to miss in the reference. For onEdit: e.source is the Spreadsheet, e.range is the edited Range object (a single cell, not a value), e.value is the new string value, e.oldValue is the previous string, and e.user is a User object. For a simple trigger you only need the fields your handler actually reads — leave out what you don't use and the call still works.

For onFormSubmit the shape is different: e.range is the row written to the response sheet, e.values is a flat array of strings in column order (index 0 is the timestamp), and e.namedValues is an object keyed by question text. If your handler reads e.namedValues['Department'][0], construct that object with the same key and a one-element array. I keep a comment block at the top of every form handler listing which fields it touches — it doubles as a template for the test function.

One field people forget: e.range for onFormSubmit is a single-row Range covering all response columns, not a single cell. If your handler calls e.range.getValues()[0] to walk the row, use sheet.getRange(lastRow, 1, 1, sheet.getLastColumn()) in the fake event so the dimensions match.

Running and verifying the test

Add testOnEdit() to any .gs file in the same project, select it in the function dropdown at the top of the editor, and click Run. Execution logs appear immediately in the console pane below. Because testOnEdit() calls onEdit(fakeEvent) directly, any Logger.log() or console.log() inside your handler shows up in that same log session — no need to dig through Stackdriver.

One practical detail: simple triggers (the built-in onEdit) run with your own account permissions, but they cannot call services that require authorization, like sending email or writing to a different spreadsheet. If your handler does either of those, it needs to be an installable trigger. The test function will still expose that error immediately — the execution will fail with a permission message — which is actually useful information you would not get from a failed silent trigger run.

Want a custom version?

Describe your sheet and the rule you want. Gnaw writes the Apps Script — fields, triggers, edge cases — in one shot.

FAQ

4 questions
Why does my trigger function work when a real edit fires but throw an error when I run it from the editor?
The editor calls the function with no arguments. Inside the handler, e is undefined, so any property access like e.range crashes immediately. Build a fake event object and pass it explicitly to the function — that is the only way to run it outside an actual trigger context.
Do I need to include every field the event object normally has?
No. Only include the fields your handler actually reads. Apps Script does not validate the shape of the object you pass, so a partial fake is fine as long as it covers every e.something your code touches.
Can I use this same approach to test onFormSubmit?
Yes, with different fields. Set e.values to an array of strings matching your form's column order, and e.namedValues to an object whose keys are your exact question texts and whose values are one-element arrays. Get the column order from the response sheet — it matches the form question order.
My test function passes but the real trigger never fires. What is wrong?
A passing test only confirms your handler logic is correct given the event shape you provided. Triggers not firing is a separate problem: check the Triggers page (clock icon in the left sidebar) to confirm the trigger is installed, verify it targets the right function name, and look at the trigger execution log for errors Apps Script may have swallowed silently.
// one good script a week

Get a working Apps Script snippet in your inbox, weekly.