Halaman ini menguraikan berbagai alat dan praktik terbaik dalam penulisan pengujian unit untuk fungsi Anda, misalnya pengujian yang akan menjadi bagian dari sistem Continuous Integration (CI). Untuk mempermudah pengujian, Firebase menyediakan Firebase Test SDK untuk Cloud Functions. Alat ini didistribusikan di npm sebagai firebase-functions-test
, dan merupakan SDK pengujian pendamping untuk firebase-functions
. Firebase Test SDK untuk Cloud Functions:
- Menangani penyiapan dan pembongkaran yang tepat untuk pengujian, seperti menetapkan dan membatalkan penetapan variabel lingkungan yang dibutuhkan oleh
firebase-functions
. - Menghasilkan data sampel dan konteks peristiwa sehingga Anda hanya perlu menentukan kolom yang relevan dengan pengujian yang dilakukan.
Penyiapan pengujian
Instal firebase-functions-test
dan Mocha, yang merupakan framework pengujian, dengan menjalankan perintah berikut di folder fungsi Anda:
npm install --save-dev firebase-functions-test
npm install --save-dev mocha
Lalu buat folder test
di dalam folder fungsi, buat file baru di dalamnya untuk kode pengujian Anda, dan beri nama, misalnya index.test.js
.
Terakhir, ubah functions/package.json
untuk menambahkan hal berikut ini:
"scripts": {
"test": "mocha --reporter spec"
}
Setelah menulis pengujian, Anda dapat menjalankannya dengan menjalankan npm test
di dalam direktori fungsi Anda.
Melakukan inisialisasi Firebase Test SDK untuk Cloud Functions
Ada dua cara untuk menggunakan firebase-functions-test
:
- Mode online (direkomendasikan): Tulis pengujian yang berinteraksi dengan project Firebase yang dikhususkan untuk pengujian sehingga penulisan database, pembuatan pengguna, dan lain-lain akan benar-benar dilakukan, dan kode pengujian Anda dapat memeriksa hasilnya. Ini juga berarti bahwa SDK Google lain yang digunakan pada fungsi Anda juga akan berfungsi.
- Mode offline: Tulis pengujian unit terisolasi dan offline tanpa efek samping. Ini berarti bahwa panggilan metode apa pun yang berinteraksi dengan produk Firebase (misalnya, menulis ke database atau membuat pengguna) harus dijadikan stub. Penggunaan mode offline biasanya tidak direkomendasikan jika Anda memiliki fungsi Cloud Firestore atau Realtime Database karena hal ini sangat meningkatkan kompleksitas kode pengujian.
Melakukan inisialisasi SDK dalam mode online (direkomendasikan)
Jika ingin menulis pengujian yang berinteraksi dengan project pengujian, Anda harus memberikan nilai konfigurasi project yang diperlukan untuk menginisialisasi aplikasi melalui firebase-admin
, dan jalur ke file kunci akun layanan.
Untuk mendapatkan nilai konfigurasi project Firebase:
- Buka setelan project Anda di Firebase console.
- Di Aplikasi Anda, pilih aplikasi yang diinginkan.
Di panel sebelah kanan, pilih opsi untuk mendownload file konfigurasi untuk aplikasi Apple dan Android.
Untuk aplikasi web, pilih Config untuk menampilkan nilai konfigurasi.
Untuk membuat file kunci:
- Buka panel Akun Layanan di Google Cloud console.
- Pilih akun layanan default App Engine dan gunakan menu opsi di sebelah kanan untuk memilih Create key.
- Saat diminta, pilih JSON untuk jenis kunci, lalu klik Create.
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');
Menginisialisasi SDK dalam mode offline
Jika ingin menulis pengujian yang sepenuhnya offline, Anda dapat menginisialisasi SDK tanpa parameter apa pun:
// At the top of test/index.test.js
const test = require('firebase-functions-test')();
Membuat nilai konfigurasi fiktif
Jika menggunakan functions.config()
dalam kode fungsi, Anda dapat membuat nilai konfigurasi fiktif. Misalnya, jika functions/index.js
berisi kode berikut:
const functions = require('firebase-functions/v1');
const key = functions.config().stripe.key;
Maka Anda dapat membuat nilai fiktif untuk nilai yang berada di dalam file pengujian seperti berikut:
// Mock functions config values
test.mockConfig({ stripe: { key: '23wr42ewr34' }});
Mengimpor fungsi
Untuk mengimpor fungsi, gunakan require
untuk mengimpor file fungsi utama Anda sebagai modul. Pastikan untuk melakukan ini hanya setelah menginisialisasi firebase-functions-test
dan membuat nilai konfigurasi fiktif.
// after firebase-functions-test has been initialized
const myFunctions = require('../index.js'); // relative path to functions code
Jika melakukan inisialisasi firebase-functions-test
dalam mode offline dan memiliki admin.initializeApp()
dalam kode fungsi, Anda harus menjadikannya stub sebelum mengimpor fungsi:
// 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 meliputi langkah-langkah berikut:
- Gabungkan fungsi yang ingin diuji dengan metode
test.wrap
. - Buat data pengujian.
- Panggil fungsi gabungan dengan data pengujian yang Anda buat dan kolom konteks peristiwa yang ingin Anda tentukan.
- Buat pernyataan tentang perilaku.
Pertama gabungkan fungsi yang ingin Anda uji. Misalnya, Anda memiliki fungsi di dalam functions/index.js
bernama makeUppercase
, yang ingin Anda uji. Tulis hal berikut 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 mengaktifkan makeUppercase
saat dipanggil. wrapped
membutuhkan 2 parameter:
- data (wajib): data yang akan dikirimkan ke
makeUppercase
. Data ini berkaitan langsung dengan parameter pertama yang dikirim ke pengendali fungsi yang Anda tulis.firebase-functions-test
menyediakan metode untuk membuat data kustom atau data contoh. - eventContextOptions (opsional): kolom konteks peristiwa yang ingin ditentukan. Konteks peristiwa adalah parameter kedua yang dikirimkan ke pengendali fungsi yang Anda tulis. Jika Anda tidak menyertakan parameter
eventContextOptions
ketika memanggilwrapped
, konteks peristiwa tetap akan dihasilkan dengan kolom yang logis. Anda dapat mengganti beberapa kolom yang dihasilkan dengan menentukannya di sini. Perhatikan bahwa Anda hanya perlu menyertakan kolom yang ingin diganti. Semua kolom yang tidak Anda ganti akan dihasilkan.
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
});
Membuat data pengujian
Parameter pertama dari fungsi gabungan adalah data pengujian yang digunakan untuk memanggil fungsi yang mendasarinya. Ada sejumlah cara untuk membuat data pengujian.
Menggunakan data kustom
firebase-functions-test
memiliki sejumlah fungsi untuk membuat data yang diperlukan untuk menguji fungsi Anda. Misalnya, gunakan test.firestore.makeDocumentSnapshot
untuk membuat DocumentSnapshot
Firestore. Argumen pertama adalah datanya, argumen kedua adalah jalur referensi lengkapnya, dan Anda dapat menentukan argumen ketiga opsional untuk properti lain snapshot.
// 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 menguji fungsi onUpdate
atau onWrite
, Anda harus membuat dua snapshot: satu untuk status sebelumnya dan satu lagi untuk status setelahnya. Kemudian, Anda dapat menggunakan metode makeChange
untuk membuat objek Change
dengan snapshot tersebut.
// 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 mengetahui fungsi serupa bagi semua jenis data lainnya.
Menggunakan data contoh
Jika Anda tidak perlu menyesuaikan data yang digunakan dalam pengujian, firebase-functions-test
menyediakan metode untuk menghasilkan data contoh bagi 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();
Baca referensi API untuk mengetahui metode mendapatkan data contoh bagi setiap jenis fungsi.
Menggunakan data yang dijadikan stub (untuk mode offline)
Jika melakukan inisialisasi SDK dalam mode offline dan menguji fungsi Cloud Firestore atau Realtime Database, Anda harus menggunakan objek biasa dengan stub, bukannya membuat DocumentSnapshot
atau DataSnapshot
sebenarnya.
Misalnya Anda sedang menulis pengujian unit 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 pengujian, buat objek biasa di mana kedua jalur kode tersebut dapat berfungsi, dan gunakan Sinon untuk membuat metodenya menjadi 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);
Membuat pernyataan
Setelah menginisialisasi SDK, menggabungkan fungsi, dan membuat data, Anda dapat memanggil fungsi gabungan dengan data yang telah dibuat dan membuat pernyataan tentang perilaku. Anda dapat menggunakan library seperti Chai untuk membuat pernyataan ini.
Membuat pernyataan pada mode online
Jika melakukan inisialisasi Firebase Test SDK untuk Cloud Functions dalam mode online, Anda dapat menyatakan bahwa tindakan yang diinginkan (seperti penulisan database) telah dilakukan menggunakan firebase-admin
SDK.
Contoh di bawah ini menyatakan bahwa 'INPUT' telah ditulis ke dalam database project pengujian.
// 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 hasil 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 spy Sinon untuk membuat pernyataan bahwa metode tertentu telah dipanggil, dan dengan parameter yang diharapkan.
Menguji fungsi HTTP
Untuk menguji fungsi HTTP onCall, gunakan pendekatan yang sama dengan pengujian fungsi latar belakang.
Jika akan menguji fungsi HTTP onRequest, sebaiknya gunakan firebase-functions-test
jika:
- Anda menggunakan
functions.config()
- Fungsi Anda berinteraksi dengan project Firebase atau Google API lainnya, dan Anda ingin menggunakan project Firebase sungguhan beserta kredensialnya untuk pengujian.
Fungsi HTTP onRequest memerlukan dua parameter: objek request dan objek response. Berikut adalah cara menguji fungsi contoh addMessage()
:
- Ganti fungsi pengalihan pada objek response, karena dipanggil oleh
sendMessage()
. - Dalam fungsi pengalihan ini, gunakan chai.assert untuk membantu membuat pernyataan tentang parameter yang akan digunakan untuk memanggil fungsi pengalihan:
// 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);
Pembersihan pengujian
Di bagian paling akhir dari kode pengujian Anda, panggil fungsi pembersihan. Fungsi ini membatalkan setelan variabel lingkungan yang ditetapkan oleh SDK saat diinisialisasi, dan menghapus aplikasi Firebase yang mungkin telah dibuat jika Anda menggunakan SDK untuk membuat DataSnapshot
Realtime Database atau DocumentSnapshot
Firestore.
test.cleanup();
Meninjau contoh lengkap dan mempelajari lebih lanjut
Anda dapat meninjau contoh lengkap di repositori GitHub Firebase.
- Menguji Realtime Database dan Fungsi HTTP pada Mode Online
- Menguji Realtime Database dan Fungsi HTTP pada Mode Offline
Untuk mempelajari lebih lanjut, baca referensi API untuk firebase-functions-test
.