google-apps-script how - Come posso testare una funzione trigger in GAS?





to debug (3)


Aggiornamento 2017: esegui il debug degli oggetti Event con Stackdriver Logging per Google Apps Script. Dalla barra dei menu dell'editor di script, View > Stackdriver Logging : View > Stackdriver Logging per visualizzare o eseguire lo streaming dei log.

console.log() scriverà messaggi di livello DEBUG

Esempio su Edit () :

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});
}

Esempio 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});
}

Esempio onChange () :

function onChange (e) {
  var debug_e = {
    authMode:  e.authMode,  
    changeType: changeType,
    user:  e.user
  }

  console.log({message: 'onChange() Event Object', eventObject: debug_e});
}

Quindi controlla i log nell'interfaccia utente di Stackdriver etichettati come stringa del message per vedere l'output

Google-apps-script supporta i Triggers che passano gli Events per attivare le funzioni. Sfortunatamente, l'ambiente di sviluppo ti permetterà di testare le funzioni senza passare dei parametri, quindi non puoi simulare un evento in questo modo. Se ci provi, ottieni un errore del tipo:

ReferenceError: 'e' is not defined.

Si potrebbe trattare l'evento come un parametro facoltativo e inserire un valore predefinito nella funzione di trigger usando una delle tecniche da " Esiste un modo migliore per eseguire parametri di funzione facoltativi in ​​Javascript? ". Ma questo introduce il rischio che un programmatore pigro (mani in alto se sei tu!) Lascerà quel codice dietro, con effetti collaterali non voluti.

Sicuramente ci sono modi migliori?




È possibile scrivere una funzione di test che passa un evento simulato alla funzione di trigger. Ecco un esempio che verifica una funzione di trigger onEdit() . Passa un oggetto evento con tutte le informazioni descritte per "Eventi di modifica del foglio di calcolo" in Events .

Per usarlo, imposta il tuo breakpoint nella funzione onEdit target, seleziona function test_onEdit e premi Debug .

/**
 * Test function for onEdit. Passes an event object to simulate an edit to
 * a cell in a spreadsheet.
 *
 * Check for updates: https://.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"
  });
}

Se sei curioso, questo è stato scritto per testare la funzione onEdit per Google Spreadsheet condizionale su tre celle .

Ecco una funzione di test per gli eventi di invio moduli di fogli di calcolo. Costruisce il suo evento simulato leggendo i dati di invio del modulo. Questo è stato originariamente scritto per ottenere TypeError nel trigger onFormSubmit? .

/**
 * Test function for Spreadsheet Form Submit trigger functions.
 * Loops through content of sheet, creating simulated Form Submit Events.
 *
 * Check for updates: https://.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://.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);
  }
}

Suggerimenti

Quando si simulano gli eventi, fare attenzione ad abbinare gli oggetti evento documentati il ​​più vicino possibile.

  • Se si desidera convalidare la documentazione, è possibile registrare l'evento ricevuto dalla funzione di trigger.

    Logger.log( JSON.stringify( e , null, 2 ) );
    
  • In eventi di invio del modulo Foglio di calcolo:

    • tutti i valori namedValues ​​sono matrici.
    • I timestamp sono stringhe e il loro formato verrà localizzato nelle impostazioni locali del modulo. Se letti da un foglio di calcolo con formattazione predefinita * , sono oggetti Data. Se la funzione di trigger si basa sul formato stringa del timestamp (che è una cattiva idea), assicurarsi di simulare il valore in modo appropriato.
    • Se nel tuo foglio di lavoro sono presenti colonne che non sono nel tuo modulo, la tecnica in questo script simulerà un "evento" con quei valori aggiuntivi inclusi, che non è quello che riceverai da un invio di modulo.
    • Come riportato nel numero 4335 , l'array di values ignora le risposte vuote (in "nuove forme" + "nuovi fogli"). Il metodo filter(Boolean) viene utilizzato per simulare questo comportamento.

* Un "testo normale" formattato in una cella manterrà la data come una stringa e non è una buona idea.




Ecco una funzione molto generale che itera sui valori di un intervallo. Può anche essere usato per fare una funzione di reduce su di esso (che è utile nel tuo caso). Può anche uscire dal ciclo se vuoi solo trovare il primo di un elemento.

Può essere facilmente modificato per accettare un'istanza Range effettiva invece della matrice di valori.

function range_reduce(rangeValues,fn,collection) {
  collection = collection || [];
  var debug_rr = "<<";
  for(var rowIndex = 0, row=undefined; rowIndex<rangeValues.length && (row = rangeValues[rowIndex]); rowIndex++) { 
    for(var colIndex = 0, value=undefined; colIndex<row.length && (value = row[colIndex]); colIndex++) {
      try {
        collection = fn(collection, value, rowIndex, colIndex);
      } catch (e) {
        if(! e instanceof BreakException) {
          throw e;
        } else {
          return collection;
        }
      }
    }
  }
  return collection;
}

// this is a created, arbitrary function to serve as a way
// to break out of the reduce function. Your callback would
// `throw new BreakException()` and `rang_reduce` would stop
// there and not continue iterating over "rangeValues".
function BreakException();

Nel tuo caso:

var range = SpreadsheetApp.getActiveSheet().getActiveRange()
var writeValues = range_reduce(range.getValues(), function(collection, value, row, col) {
    collection[row] || collection.push([]);
    collection[row].push(value + " string");
});
range.setValues(writeValues)






google-apps-script google-spreadsheet