Google is committed to advancing racial equity for Black communities. See how.
Questa pagina è stata tradotta dall'API Cloud Translation.
Switch to English

Test di unità di Cloud Functions

In questa pagina vengono descritte le procedure consigliate e gli strumenti per la scrittura di unit test per le funzioni, ad esempio i test che farebbero parte di un sistema di integrazione continua (CI). Per semplificare i test, Firebase fornisce Firebase Test SDK per Cloud Functions. È distribuito su npm come firebase-functions-test ed è un SDK di test complementare per firebase-functions . Firebase Test SDK per Cloud Functions:

  • Si prende cura della configurazione e dello smontaggio appropriati per i tuoi test, come l'impostazione e la disattivazione delle variabili di ambiente necessarie per le firebase-functions .
  • Genera dati di esempio e contesto dell'evento, quindi devi solo specificare i campi rilevanti per il tuo test.

Configurazione di prova

Installa sia firebase-functions-test che Mocha , un framework di test, eseguendo i seguenti comandi nella cartella delle funzioni:

npm install --save-dev firebase-functions-test
npm install --save-dev mocha

Quindi crea una cartella di test all'interno della cartella delle funzioni, crea un nuovo file al suo interno per il tuo codice di prova e index.test.js come index.test.js .

Infine, modifica functions/package.json per aggiungere quanto segue:

"scripts": {
  "test": "mocha --reporter spec"
}

Dopo aver scritto i test, è possibile eseguirli eseguendo npm test nella directory delle funzioni.

Inizializzazione dell'SDK di Firebase Test per Cloud Functions

Esistono due modi per utilizzare firebase-functions-test :

  1. Modalità online (consigliata): scrivi test che interagiscono con un progetto Firebase dedicato ai test in modo che le scritture sul database, le creazioni degli utenti, ecc. Avvengano effettivamente e il codice del test possa ispezionare i risultati. Ciò significa anche che funzioneranno anche altri SDK di Google utilizzati nelle tue funzioni.
  2. Modalità offline: scrivi unit test in silos e offline senza effetti collaterali. Ciò significa che qualsiasi chiamata di metodo che interagisce con un prodotto Firebase (ad esempio, la scrittura nel database o la creazione di un utente) deve essere bloccata. L'utilizzo della modalità offline in genere non è consigliato se si dispone delle funzioni Cloud Firestore o Realtime Database, poiché aumenta notevolmente la complessità del codice di test.

Inizializza SDK in modalità online (consigliato)

Se desideri scrivere test che interagiscono con un progetto di test, devi fornire i valori di configurazione del progetto necessari per inizializzare l'app tramite firebase-admin e il percorso di un file di chiave dell'account di servizio.

Per ottenere i valori di configurazione del tuo progetto Firebase:

  1. Apri le impostazioni del progetto nella console Firebase .
  2. In Le tue app, seleziona l'app desiderata.
  3. Nel riquadro di destra, seleziona l'opzione per scaricare un file di configurazione per le app iOS e Android.

    Per le app Web, seleziona Config per visualizzare i valori di configurazione.

Per creare un file chiave:

  1. Apri il riquadro Account di servizio della Google Cloud Console.
  2. Seleziona l'account di servizio predefinito di App Engine e utilizza il menu delle opzioni a destra per selezionare Crea chiave .
  3. Quando richiesto, seleziona JSON per il tipo di chiave e fai clic su Crea .

Dopo aver salvato il file della chiave, inizializza l'SDK:

// At the top of test/index.test.js
const test = require('firebase-functions-test')({
  databaseURL: 'https://my-project.firebaseio.com',
  storageBucket: 'my-project.appspot.com',
  projectId: 'my-project',
}, 'path/to/serviceAccountKey.json');

Inizializza SDK in modalità offline

Se desideri scrivere test completamente offline, puoi inizializzare l'SDK senza alcun parametro:

// At the top of test/index.test.js
const test = require('firebase-functions-test')();

Valori di configurazione beffardi

Se usi functions.config() nel codice delle funzioni, puoi deridere i valori di configurazione. Ad esempio, se functions/index.js contiene il codice seguente:

const functions = require('firebase-functions');
const key = functions.config().stripe.key;

Quindi puoi deridere il valore all'interno del tuo file di test in questo modo:

// Mock functions config values
test.mockConfig({ stripe: { key: '23wr42ewr34' }});

Importare le tue funzioni

Per importare le tue funzioni, usa require per importare il tuo file delle funzioni principali come modulo. Assicurati di farlo solo dopo aver inizializzato firebase-functions-test e deriso i valori di configurazione.

// after firebase-functions-test has been initialized
const myFunctions = require('../index.js'); // relative path to functions code

Se hai inizializzato firebase-functions-test in modalità offline e hai admin.initializeApp() nel codice delle funzioni, devi bloccarlo prima di importare le tue funzioni:

// If index.js calls admin.initializeApp at the top of the file,
// we need to stub it out before requiring index.js. This is because the
// functions will be executed as a part of the require process.
// Here we stub admin.initializeApp to be a dummy function that doesn't do anything.
adminInitStub = sinon.stub(admin, 'initializeApp');
// Now we can require index.js and save the exports inside a namespace called myFunctions.
myFunctions = require('../index');

Test delle funzioni in background (non HTTP)

Il processo per testare le funzioni non HTTP prevede i seguenti passaggi:

  1. Racchiudi la funzione che desideri testare con il metodo test.wrap
  2. Costruisci dati di test
  3. Richiama la funzione a capo con i dati di test che hai costruito e tutti i campi del contesto dell'evento che desideri specificare.
  4. Fai affermazioni sul comportamento.

Innanzitutto avvolgi la funzione che desideri testare. Supponiamo che tu abbia una funzione in functions/index.js chiamata makeUppercase , che vorresti testare. Scrivi quanto segue in functions/test/index.test.js

// "Wrap" the makeUpperCase function from index.js
const myFunctions = require('../index.js');
const wrapped = test.wrap(myFunctions.makeUppercase);

wrapped è una funzione che invoca makeUppercase quando viene chiamata. wrapped richiede 2 parametri:

  1. data (obbligatorio): i dati da inviare a makeUppercase . Questo corrisponde direttamente al primo parametro inviato al gestore della funzione che hai scritto. firebase-functions-test fornisce metodi per costruire dati personalizzati o dati di esempio.
  2. eventContextOptions (facoltativo): campi del contesto dell'evento che desideri specificare. Il contesto dell'evento è il secondo parametro inviato al gestore della funzione che hai scritto. Se non si include un parametro eventContextOptions durante la chiamata a wrapped , viene comunque generato un contesto di evento con campi sensibili. Puoi sovrascrivere alcuni dei campi generati specificandoli qui. Tieni presente che devi includere solo i campi che desideri sostituire. Tutti i campi che non hai sovrascritto vengono generati.
const data = … // See next section for constructing test data

// Invoke the wrapped function without specifying the event context.
wrapped(data);

// Invoke the function, and specify params
wrapped(data, {
  params: {
    pushId: '234234'
  }
});

// Invoke the function, and specify auth and auth Type (for real time database functions only)
wrapped(data, {
  auth: {
    uid: 'jckS2Q0'
  },
  authType: 'USER'
});

// Invoke the function, and specify all the fields that can be specified
wrapped(data, {
  eventId: 'abc',
  timestamp: '2018-03-23T17:27:17.099Z',
  params: {
    pushId: '234234'
  },
  auth: {
    uid: 'jckS2Q0' // only for real time database functions
  },
  authType: 'USER' // only for real time database functions
});

Costruire dati di test

Il primo parametro di una funzione racchiusa sono i dati di test con cui richiamare la funzione sottostante. Esistono diversi modi per creare dati di test.

Utilizzo di dati personalizzati

firebase-functions-test ha un numero di funzioni per costruire i dati necessari per testare le tue funzioni. Ad esempio, utilizza test.firestore.makeDocumentSnapshot per creare un Firestore DocumentSnapshot . Il primo argomento sono i dati e il secondo argomento è il percorso di riferimento completo e c'è un terzo argomento opzionale per altre proprietà dell'istantanea che è possibile specificare.

// Make snapshot
const snap = test.firestore.makeDocumentSnapshot({foo: 'bar'}, 'document/path');
// Call wrapped function with the snapshot
const wrapped = test.wrap(myFunctions.myFirestoreDeleteFunction);
wrapped(snap);

Se stai testando una funzione onUpdate o onWrite , dovrai creare due snapshot: uno per lo stato prima e uno per lo stato dopo. Quindi, puoi utilizzare il metodo makeChange per creare un oggetto Change con queste istantanee.

// Make snapshot for state of database beforehand
const beforeSnap = test.firestore.makeDocumentSnapshot({foo: 'bar'}, 'document/path');
// Make snapshot for state of database after the change
const afterSnap = test.firestore.makeDocumentSnapshot({foo: 'faz'}, 'document/path');
const change = test.makeChange(beforeSnap, afterSnap);
// Call wrapped function with the Change object
const wrapped = test.wrap(myFunctions.myFirestoreUpdateFunction);
wrapped(change);

Consulta il riferimento API per funzioni simili per tutti gli altri tipi di dati.

Utilizzo di dati di esempio

Se non è necessario personalizzare i dati utilizzati nei test, firebase-functions-test offre metodi per generare dati di esempio per ogni tipo di funzione.

// For Firestore onCreate or onDelete functions
const snap = test.firestore.exampleDocumentSnapshot();
// For Firestore onUpdate or onWrite functions
const change = test.firestore.exampleDocumentSnapshotChange();

Vedere il riferimento API per i metodi per ottenere dati di esempio per ogni tipo di funzione.

Utilizzo di dati stub (per la modalità offline)

Se hai inizializzato l'SDK in modalità offline e stai testando una funzione Cloud Firestore o Realtime Database, dovresti utilizzare un oggetto semplice con stub invece di creare un DocumentSnapshot o DataSnapshot effettivo.

Supponiamo che tu stia scrivendo uno unit test per la seguente funzione:

// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
    .onCreate((snapshot, context) => {
      // Grab the current value of what was written to the Realtime Database.
      const original = snapshot.val();
      console.log('Uppercasing', context.params.pushId, original);
      const uppercase = original.toUpperCase();
      // You must return a Promise when performing asynchronous tasks inside a Functions such as
      // writing to the Firebase Realtime Database.
      // Setting an "uppercase" sibling in the Realtime Database returns a Promise.
      return snapshot.ref.parent.child('uppercase').set(uppercase);
    });

All'interno della funzione, lo snap viene utilizzato due volte:

  • snap.val()
  • snap.ref.parent.child('uppercase').set(uppercase)

Nel codice di test, creare un oggetto semplice in cui funzioneranno entrambi questi percorsi di codice e utilizzare Sinon per stub i metodi.

// The following lines creates a fake snapshot, 'snap', which returns 'input' when snap.val() is called,
// and returns true when snap.ref.parent.child('uppercase').set('INPUT') is called.
const snap = {
  val: () => 'input',
  ref: {
    parent: {
      child: childStub,
    }
  }
};
childStub.withArgs(childParam).returns({ set: setStub });
setStub.withArgs(setParam).returns(true);

Fare affermazioni

Dopo aver inizializzato l'SDK, avvolto le funzioni e costruito i dati, è possibile richiamare le funzioni incluse con i dati costruiti e fare asserzioni sul comportamento. Puoi usare una libreria come Chai per fare queste affermazioni.

Fare affermazioni in modalità online

Se hai inizializzato Firebase Test SDK per Cloud Functions in modalità online , puoi affermare che le azioni desiderate (come una scrittura sul database) sono state eseguite utilizzando firebase-admin SDK.

L'esempio seguente afferma che "INPUT" è stato scritto nel database del progetto di test.

// Create a DataSnapshot with the value 'input' and the reference path 'messages/11111/original'.
const snap = test.database.makeDataSnapshot('input', 'messages/11111/original');

// Wrap the makeUppercase function
const wrapped = test.wrap(myFunctions.makeUppercase);
// Call the wrapped function with the snapshot you constructed.
return wrapped(snap).then(() => {
  // Read the value of the data at messages/11111/uppercase. Because `admin.initializeApp()` is
  // called in functions/index.js, there's already a Firebase app initialized. Otherwise, add
  // `admin.initializeApp()` before this line.
  return admin.database().ref('messages/11111/uppercase').once('value').then((createdSnap) => {
    // Assert that the value is the uppercased version of our input.
    assert.equal(createdSnap.val(), 'INPUT');
  });
});

Fare affermazioni in modalità offline

Puoi fare asserzioni sul valore di ritorno atteso della funzione:

const childParam = 'uppercase';
const setParam = 'INPUT';
// Stubs are objects that fake and/or record function calls.
// These are excellent for verifying that functions have been called and to validate the
// parameters passed to those functions.
const childStub = sinon.stub();
const setStub = sinon.stub();
// The following lines creates a fake snapshot, 'snap', which returns 'input' when snap.val() is called,
// and returns true when snap.ref.parent.child('uppercase').set('INPUT') is called.
const snap = {
  val: () => 'input',
  ref: {
    parent: {
      child: childStub,
    }
  }
};
childStub.withArgs(childParam).returns({ set: setStub });
setStub.withArgs(setParam).returns(true);
// Wrap the makeUppercase function.
const wrapped = test.wrap(myFunctions.makeUppercase);
// Since we've stubbed snap.ref.parent.child(childParam).set(setParam) to return true if it was
// called with the parameters we expect, we assert that it indeed returned true.
return assert.equal(wrapped(snap), true);

Puoi anche usare le spie Sinon per affermare che alcuni metodi sono stati chiamati e con i parametri che ti aspetti.

Test delle funzioni HTTP

Per testare le funzioni HTTP onCall, utilizzare lo stesso approccio del test delle funzioni in background .

Se stai testando le funzioni HTTP onRequest, dovresti usare firebase-functions-test se:

  • Usi functions.config()
  • La tua funzione interagisce con un progetto Firebase o altre API di Google e desideri utilizzare un vero progetto Firebase e le sue credenziali per i tuoi test.

Una funzione HTTP onRequest accetta due parametri: un oggetto richiesta e un oggetto risposta. Ecco come potresti testare la funzione di esempio addMessage() :

  • Ignora la funzione di reindirizzamento nell'oggetto risposta, poiché sendMessage() chiama.
  • All'interno della funzione di reindirizzamento, usa chai.assert per fare asserzioni su quali parametri dovrebbe essere chiamata la funzione di reindirizzamento:
// A fake request object, with req.query.text set to 'input'
const req = { query: {text: 'input'} };
// A fake response object, with a stubbed redirect function which asserts that it is called
// with parameters 303, 'new_ref'.
const res = {
  redirect: (code, url) => {
    assert.equal(code, 303);
    assert.equal(url, 'new_ref');
    done();
  }
};

// Invoke addMessage with our fake request and response objects. This will cause the
// assertions in the response object to be evaluated.
myFunctions.addMessage(req, res);

Prova la pulizia

Alla fine del codice di prova, chiama la funzione di pulizia. Ciò annulla le variabili di ambiente impostate dall'SDK quando è stato inizializzato ed elimina le app Firebase che potrebbero essere state create se hai utilizzato l'SDK per creare un database in tempo reale DataSnapshot o Firestore DocumentSnapshot .

test.cleanup();

Rivedi esempi completi e scopri di più

Puoi rivedere gli esempi completi nel repository Firebase GitHub.

Per ulteriori informazioni, firebase-functions-test riferimento al riferimento API per firebase-functions-test .