Ikuti semua informasi yang diumumkan di Firebase Summit, dan pelajari bagaimana Firebase dapat membantu Anda mempercepat pengembangan aplikasi dan menjalankan aplikasi dengan percaya diri. Pelajari Lebih Lanjut

Pengujian unit Cloud Functions

Halaman ini menjelaskan praktik terbaik dan alat untuk menulis pengujian unit untuk fungsi Anda, seperti pengujian yang akan menjadi bagian dari sistem Continuous Integration (CI). Untuk mempermudah pengujian, Firebase menyediakan Firebase Test SDK untuk Cloud Functions. Ini didistribusikan di npm sebagai firebase-functions-test , dan merupakan SDK uji pendamping untuk firebase-functions . Firebase Test SDK untuk Cloud Functions:

  • Menangani penyiapan dan pembongkaran yang sesuai untuk pengujian Anda, seperti menyetel dan menghapus setelan variabel lingkungan yang diperlukan oleh firebase-functions .
  • Menghasilkan data sampel dan konteks peristiwa, sehingga Anda hanya perlu menentukan bidang yang relevan dengan pengujian Anda.

Pengaturan tes

Instal firebase-functions-test dan Mocha , kerangka kerja pengujian, dengan menjalankan perintah berikut di folder fungsi Anda:

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

Selanjutnya buat folder test di dalam folder fungsi, buat file baru di dalamnya untuk kode pengujian Anda, dan beri nama seperti index.test.js .

Terakhir, ubah functions/package.json untuk menambahkan yang berikut:

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

Setelah Anda menulis tes, Anda dapat menjalankannya dengan menjalankan npm test di dalam direktori fungsi Anda.

Menginisialisasi Firebase Test SDK untuk Cloud Functions

Ada dua cara untuk menggunakan firebase-functions-test :

  1. Mode online (disarankan): Tulis pengujian yang berinteraksi dengan proyek Firebase yang didedikasikan untuk pengujian sehingga penulisan database, pembuatan pengguna, dll. benar-benar terjadi, dan kode pengujian Anda dapat memeriksa hasilnya. Ini juga berarti bahwa Google SDK lain yang digunakan dalam fungsi Anda juga akan berfungsi.
  2. Mode offline: Tulis pengujian unit secara diam dan offline tanpa efek samping. Ini berarti bahwa setiap panggilan metode yang berinteraksi dengan produk Firebase (misalnya menulis ke database atau membuat pengguna) harus di-stub. Menggunakan mode offline umumnya tidak disarankan jika Anda memiliki fungsi Cloud Firestore atau Realtime Database, karena ini sangat meningkatkan kerumitan kode pengujian Anda.

Inisialisasi SDK dalam mode online (disarankan)

Jika Anda ingin menulis pengujian yang berinteraksi dengan proyek pengujian, Anda perlu menyediakan nilai konfigurasi proyek yang diperlukan untuk menginisialisasi aplikasi melalui firebase-admin , dan jalur ke file kunci akun layanan.

Untuk mendapatkan nilai konfigurasi proyek Firebase Anda:

  1. Buka setelan proyek Anda di konsol Firebase .
  2. Di Aplikasi Anda, pilih aplikasi yang diinginkan.
  3. Di panel kanan, pilih opsi untuk mengunduh file konfigurasi untuk aplikasi Apple dan Android.

    Untuk aplikasi web, pilih Config untuk menampilkan nilai konfigurasi.

Untuk membuat file kunci:

  1. Buka panel Akun Layanan dari Google Cloud Console.
  2. Pilih akun layanan default App Engine, dan gunakan menu opsi di sebelah kanan untuk memilih Buat kunci .
  3. Saat diminta, pilih JSON untuk jenis kunci, dan klik Buat .

Setelah menyimpan file kunci, inisialisasi 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');

Inisialisasi SDK dalam mode offline

Jika Anda ingin menulis pengujian offline sepenuhnya, Anda dapat menginisialisasi SDK tanpa parameter apa pun:

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

Mengejek nilai konfigurasi

Jika Anda menggunakan functions.config() dalam kode fungsi Anda, Anda dapat mengejek nilai konfigurasi. Misalnya, jika functions/index.js berisi kode berikut:

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

Kemudian Anda dapat mengejek nilai di dalam file pengujian Anda seperti:

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

Mengimpor fungsi Anda

Untuk mengimpor fungsi Anda, gunakan require untuk mengimpor file fungsi utama Anda sebagai modul. Pastikan untuk hanya melakukan ini setelah menginisialisasi firebase-functions-test , dan mengejek nilai konfigurasi.

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

Jika Anda menginisialisasi firebase-functions-test dalam mode offline , dan Anda memiliki admin.initializeApp() dalam kode fungsi Anda, maka Anda perlu mematikannya sebelum mengimpor fungsi Anda:

// 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');

Menguji fungsi latar belakang (non-HTTP)

Proses untuk menguji fungsi non-HTTP melibatkan langkah-langkah berikut:

  1. Bungkus fungsi yang ingin Anda uji dengan metode test.wrap
  2. Buat data uji
  3. Panggil fungsi yang dibungkus dengan data pengujian yang Anda buat dan bidang konteks peristiwa apa pun yang ingin Anda tentukan.
  4. Buat pernyataan tentang perilaku.

Bungkus dulu fungsi yang ingin Anda uji. Katakanlah Anda memiliki fungsi di functions/index.js yang disebut makeUppercase , yang ingin Anda uji. Tulis yang berikut ini di functions/test/index.test.js

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

wrapped adalah fungsi yang memanggil makeUppercase saat dipanggil. wrapped membutuhkan 2 parameter:

  1. data (wajib): data yang akan dikirim ke makeUppercase . Ini secara langsung sesuai dengan parameter pertama yang dikirim ke pengendali fungsi yang Anda tulis. firebase-functions-test menyediakan metode untuk membuat data khusus atau data contoh.
  2. eventContextOptions (opsional): bidang konteks peristiwa yang ingin Anda tentukan. Konteks peristiwa adalah parameter kedua yang dikirim ke pengendali fungsi yang Anda tulis. Jika Anda tidak menyertakan parameter eventContextOptions saat memanggil wrapped , konteks peristiwa masih dibuat dengan bidang yang masuk akal. Anda dapat mengganti beberapa bidang yang dihasilkan dengan menentukannya di sini. Perhatikan bahwa Anda hanya perlu menyertakan bidang yang ingin Anda timpa. Bidang apa pun yang tidak Anda timpa akan dibuat.
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
});

Membangun data uji

Parameter pertama dari fungsi yang dibungkus adalah data uji untuk memanggil fungsi yang mendasarinya. Ada beberapa cara untuk membangun data uji.

Menggunakan data khusus

firebase-functions-test memiliki sejumlah fungsi untuk membangun data yang diperlukan untuk menguji fungsi Anda. Misalnya, gunakan test.firestore.makeDocumentSnapshot untuk membuat Firestore DocumentSnapshot . Argumen pertama adalah data, dan argumen kedua adalah jalur referensi lengkap, dan ada argumen ketiga opsional untuk properti lain dari snapshot yang dapat Anda tentukan.

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

Jika Anda menguji fungsi onUpdate atau onWrite , Anda harus membuat dua snapshot: satu untuk status sebelum dan satu untuk status setelah. Kemudian, Anda dapat menggunakan metode makeChange untuk membuat objek Change dengan snapshot ini.

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

Lihat referensi API untuk fungsi serupa untuk semua tipe data lainnya.

Menggunakan contoh data

Jika Anda tidak perlu menyesuaikan data yang digunakan dalam pengujian Anda, maka firebase-functions-test menawarkan metode untuk menghasilkan data contoh untuk setiap jenis fungsi.

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

Lihat referensi API untuk metode mendapatkan contoh data untuk setiap tipe fungsi.

Menggunakan data yang terputus (untuk mode offline)

Jika Anda menginisialisasi SDK dalam mode offline, dan sedang menguji fungsi Cloud Firestore atau Realtime Database, Anda harus menggunakan objek biasa dengan stub daripada membuat DocumentSnapshot atau DataSnapshot yang sebenarnya.

Katakanlah Anda sedang menulis unit test untuk fungsi berikut:

// 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();
      functions.logger.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);
    });

Di dalam fungsi, snap digunakan dua kali:

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

Dalam kode uji, buat objek biasa di mana kedua jalur kode ini akan bekerja, dan gunakan Sinon untuk mematikan metode.

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

Membuat pernyataan

Setelah menginisialisasi SDK, membungkus fungsi, dan membuat data, Anda dapat memanggil fungsi yang dibungkus dengan data yang dibuat dan membuat pernyataan tentang perilaku. Anda dapat menggunakan perpustakaan seperti Chai untuk membuat pernyataan ini.

Membuat pernyataan dalam mode online

Jika Anda menginisialisasi Firebase Test SDK for Cloud Functions dalam mode online , Anda dapat menegaskan bahwa tindakan yang diinginkan (seperti penulisan database) telah dilakukan dengan menggunakan SDK firebase-admin .

Contoh di bawah ini menegaskan bahwa 'INPUT' telah ditulis ke dalam database proyek uji.

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

Membuat pernyataan dalam mode offline

Anda dapat membuat pernyataan tentang nilai pengembalian yang diharapkan dari fungsi:

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

Anda juga dapat menggunakan mata- mata Sinon untuk menegaskan bahwa metode tertentu telah dipanggil, dan dengan parameter yang Anda harapkan.

Menguji fungsi HTTP

Untuk menguji fungsi HTTP onCall, gunakan pendekatan yang sama seperti menguji fungsi latar belakang .

Jika Anda menguji fungsi HTTP onRequest, Anda harus menggunakan firebase-functions-test jika:

  • Anda menggunakan functions.config()
  • Fungsi Anda berinteraksi dengan proyek Firebase atau Google API lainnya, dan Anda ingin menggunakan proyek Firebase yang sebenarnya dan kredensialnya untuk pengujian Anda.

Fungsi HTTP onRequest mengambil dua parameter: objek permintaan dan objek respons. Inilah cara Anda menguji fungsi contoh addMessage() :

  • Ganti fungsi pengalihan di objek respons, karena sendMessage() memanggilnya.
  • Dalam fungsi redirect, gunakan chai.assert untuk membantu membuat pernyataan tentang parameter apa yang harus dipanggil dengan fungsi redirect:
// 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);

Uji pembersihan

Di akhir kode pengujian Anda, panggil fungsi pembersihan. Ini menghapus variabel lingkungan yang ditetapkan SDK saat diinisialisasi, dan menghapus aplikasi Firebase yang mungkin telah dibuat jika Anda menggunakan SDK untuk membuat database waktu nyata DataSnapshot atau Firestore DocumentSnapshot .

test.cleanup();

Tinjau contoh lengkap dan pelajari lebih lanjut

Anda dapat meninjau contoh lengkapnya di repositori Firebase GitHub.

Untuk mempelajari lebih lanjut, lihat referensi API untuk firebase-functions-test .