Bu sayfada, Sürekli Entegrasyon (CI) sisteminin bir parçası olacak testler gibi, işlevleriniz için birim testleri yazmak için en iyi uygulamalar ve araçlar açıklanmaktadır. Firebase, testi kolaylaştırmak amacıyla Cloud Functions için Firebase Test SDK'sı sağlar. Npm'de firebase-functions-test
olarak dağıtılır ve firebase-functions-test
için tamamlayıcı bir test firebase-functions
. Cloud Functions için Firebase Test SDK'sı:
-
firebase-functions
tarafından ihtiyaç duyulan ortam değişkenlerinin ayarlanması ve ayarlarınınfirebase-functions
gibi testleriniz için uygun kurulum vefirebase-functions
. - Örnek veriler ve olay bağlamı oluşturur, böylece yalnızca testinizle ilgili alanları belirtmeniz gerekir.
Test kurulumu
Her iki yükleme firebase-functions-test
ve Mocha klasör fonksiyonları aşağıdaki komutları çalıştırarak, bir test çerçevesi,:
npm install --save-dev firebase-functions-test
npm install --save-dev mocha
Ardından, işlevler klasörünün içinde bir test
klasörü oluşturun, içinde test kodunuz için yeni bir dosya oluşturun ve bunu index.test.js
gibi bir index.test.js
.
Son olarak, aşağıdakileri eklemek için functions/package.json
değiştirin:
"scripts": {
"test": "mocha --reporter spec"
}
Testleri yazdıktan sonra, fonksiyonlar dizininizde npm test
çalıştırarak çalıştırabilirsiniz.
Cloud Functions için Firebase Test SDK'sını Başlatma
firebase-functions-test
kullanmanın iki yolu vardır:
- Çevrimiçi mod (önerilir): Test etmeye adanmış bir Firebase projesiyle etkileşime giren testler yazın, böylece veritabanı yazma, kullanıcı oluşturma vb. Gerçekte gerçekleşir ve test kodunuz sonuçları inceleyebilir. Bu aynı zamanda işlevlerinizde kullanılan diğer Google SDK'larının da çalışacağı anlamına gelir.
- Çevrimdışı mod: Hiçbir yan etkisi olmadan silo ve çevrimdışı birim testleri yazın. Bu, bir Firebase ürünüyle etkileşime giren herhangi bir yöntem çağrısının (örneğin, veritabanına yazmak veya bir kullanıcı oluşturmak) stub'a tabi tutulması gerektiği anlamına gelir. Test kodunuzun karmaşıklığını büyük ölçüde artırdığı için, Cloud Firestore veya Realtime Database işlevleriniz varsa çevrimdışı modun kullanılması genellikle önerilmez.
SDK'yı çevrimiçi modda başlatın (önerilir)
Bir test projesiyle etkileşime giren testler yazmak isterseniz, uygulamayı firebase-admin
aracılığıyla başlatmak için gereken proje yapılandırma değerlerini ve bir hizmet hesabı anahtar dosyasına giden yolu sağlamanız gerekir.
Firebase projenizin yapılandırma değerlerini almak için:
- Firebase konsolunda proje ayarlarınızı açın.
- Uygulamalarınız'da istediğiniz uygulamayı seçin.
Sağ bölmede, iOS ve Android uygulamaları için bir yapılandırma dosyası indirme seçeneğini seçin.
Web uygulamaları için, yapılandırma değerlerini görüntülemek için Yapılandır öğesini seçin.
Bir anahtar dosyası oluşturmak için:
- Google Cloud Console'un Hizmet Hesapları bölümünü açın.
- App Engine varsayılan hizmet hesabını seçin ve Anahtar oluştur'u seçmek için sağdaki seçenekler menüsünü kullanın.
- İstendiğinde, anahtar türü için JSON'u seçin ve Oluştur'u tıklayın.
Anahtar dosyasını kaydettikten sonra, SDK'yı başlatın:
// 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');
SDK'yı çevrimdışı modda başlatın
Tamamen çevrimdışı testler yazmak isterseniz, SDK'yı herhangi bir parametre olmadan başlatabilirsiniz:
// At the top of test/index.test.js
const test = require('firebase-functions-test')();
Alay yapılandırma değerleri
İşlev kodunuzda functions.config()
kullanırsanız, yapılandırma değerleriyle alay edebilirsiniz. Örneğin, functions/index.js
aşağıdaki kodu içeriyorsa:
const functions = require('firebase-functions');
const key = functions.config().stripe.key;
Ardından, test dosyanızın içindeki değerle şu şekilde taklit edebilirsiniz:
// Mock functions config values
test.mockConfig({ stripe: { key: '23wr42ewr34' }});
İşlevlerinizi içe aktarma
İşlevlerinizi içe aktarmak için, ana işlevler dosyanızı bir modül olarak içe aktarmayı require
kullanın. Bunu yalnızca firebase-functions-test
başlattıktan ve yapılandırma değerleriyle alay ettikten sonra yaptığınızdan emin olun.
// after firebase-functions-test has been initialized
const myFunctions = require('../index.js'); // relative path to functions code
firebase-functions-test
çevrimdışı modda admin.initializeApp()
ve işlev kodunuzda admin.initializeApp()
varsa, işlevlerinizi içe aktarmadan önce onu saplamanız gerekir:
// 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');
Arka plan (HTTP olmayan) işlevleri test etme
HTTP olmayan işlevleri test etme süreci aşağıdaki adımları içerir:
- Test etmek istediğiniz
test.wrap
yöntemiyletest.wrap
- Test verilerini oluşturun
- Oluşturduğunuz test verileri ve belirtmek istediğiniz herhangi bir olay bağlamı alanlarıyla sarmalanmış işlevi çağırın.
- Davranış hakkında iddialarda bulunun.
Önce test etmek istediğiniz işlevi kaydırın. Diyelim ki functions/index.js
test etmek istediğiniz makeUppercase
adlı bir fonksiyonunuz var. Aşağıdakileri functions/test/index.test.js
// "Wrap" the makeUpperCase function from index.js
const myFunctions = require('../index.js');
const wrapped = test.wrap(myFunctions.makeUppercase);
wrapped
, çağrıldığında makeUppercase
çağıran bir makeUppercase
. wrapped
2 parametre alır:
- data (gerekli):
makeUppercase
gönderilecek veriler. Bu, yazdığınız işlev işleyicisine gönderilen ilk parametreye doğrudan karşılık gelir.firebase-functions-test
, özel veriler veya örnek veriler oluşturmak için yöntemler sağlar. - eventContextOptions (isteğe bağlı): belirtmek istediğiniz olay bağlamının alanları. Olay bağlamı, yazdığınız işlev işleyicisine gönderilen ikinci parametredir. Bir dahil etmezseniz
eventContextOptions
çağrılırken parametrewrapped
, bir olay bağlam hala mantıklı alanları ile oluşturulur. Oluşturulan alanlardan bazılarını burada belirterek geçersiz kılabilirsiniz. Yalnızca geçersiz kılmak istediğiniz alanları eklemeniz gerektiğini unutmayın. Geçersiz kılmadığınız tüm alanlar oluşturulur.
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
});
Test verilerinin oluşturulması
Sarılmış bir işlevin ilk parametresi, temeldeki işlevi çağırmak için test verileridir. Test verilerini oluşturmanın birkaç yolu vardır.
Özel verileri kullanma
firebase-functions-test
, işlevlerinizi test etmek için gereken verileri oluşturmak için bir dizi işleve sahiptir. Örneğin, bir Firestore DocumentSnapshot
oluşturmak için test.firestore.makeDocumentSnapshot
kullanın. İlk bağımsız değişken verilerdir ve ikinci bağımsız değişken tam başvuru yoludur ve anlık görüntünün diğer özellikleri için belirtebileceğiniz isteğe bağlı üçüncü bir bağımsız değişken vardır .
// 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);
Bir onUpdate
veya onWrite
işlevini test ediyorsanız, iki anlık görüntü oluşturmanız gerekir: biri önceki durum ve diğeri sonraki durum için. Ardından, bu anlık görüntülerle bir Change
nesnesi oluşturmak için makeChange
yöntemini kullanabilirsiniz.
// 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);
Diğer tüm veri türleri için benzer işlevler için API referansına bakın.
Örnek verileri kullanma
Testlerinizde kullanılan verileri özelleştirmeniz gerekmiyorsa, firebase-functions-test
her bir işlev türü için örnek veriler oluşturmak için yöntemler sunar.
// For Firestore onCreate or onDelete functions
const snap = test.firestore.exampleDocumentSnapshot();
// For Firestore onUpdate or onWrite functions
const change = test.firestore.exampleDocumentSnapshotChange();
Her işlev türü için örnek verileri almaya yönelik yöntemler için API referansına bakın.
Aşınmış verileri kullanma (çevrimdışı mod için)
SDK'yı çevrimdışı modda başlattıysanız ve bir Cloud Firestore veya Realtime Database işlevini test ediyorsanız, gerçek bir DocumentSnapshot
veya DataSnapshot
oluşturmak yerine saplamalara sahip düz bir nesne kullanmalısınız.
Aşağıdaki fonksiyon için bir birim testi yazdığınızı varsayalım:
// 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); });
İşlevin içinde snap
iki kez kullanılır:
-
snap.val()
-
snap.ref.parent.child('uppercase').set(uppercase)
Test kodunda, bu kod yollarının her ikisinin de çalışacağı düz bir nesne oluşturun ve yöntemleri saplamak için Sinon kullanın.
// 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);
İddia yapmak
SDK'yı başlattıktan, işlevleri sarmaladıktan ve verileri oluşturduktan sonra, sarılmış işlevleri yapılandırılmış verilerle çağırabilir ve davranış hakkında iddialarda bulunabilirsiniz. Bu iddiaları yapmak için Chai gibi bir kitaplık kullanabilirsiniz.
Çevrimiçi modda iddialarda bulunmak
Cloud Functions için Firebase Test SDK'sını çevrimiçi modda firebase-admin
, firebase-admin
SDK kullanarak istenen işlemlerin (veritabanına yazma gibi) gerçekleştiğini iddia edebilirsiniz.
Aşağıdaki örnek, test projesinin veritabanına 'GİRİŞ'in yazıldığını iddia etmektedir.
// 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'); }); });
Çevrimdışı modda iddialarda bulunmak
Fonksiyonun beklenen dönüş değeri hakkında iddialarda bulunabilirsiniz:
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);
Sinon casuslarını , belirli yöntemlerin çağrıldığını ve beklediğiniz parametrelerle iddia etmek için de kullanabilirsiniz.
HTTP işlevlerini test etme
HTTP onCall işlevlerini test etmek için, arka plan işlevlerini test etmeyle aynı yaklaşımı kullanın.
HTTP onRequest işlevlerini firebase-functions-test
ediyorsanız, aşağıdaki durumlarda firebase-functions-test
kullanmalısınız:
-
functions.config()
- İşleviniz bir Firebase projesi veya diğer Google API'leri ile etkileşime giriyor ve testleriniz için gerçek bir Firebase projesi ve onun kimlik bilgilerini kullanmak istiyorsunuz.
Bir HTTP onRequest işlevi iki parametre alır: bir istek nesnesi ve bir yanıt nesnesi. addMessage()
örnek işlevini şu şekilde test edebilirsiniz:
-
sendMessage()
çağırdığı için yanıt nesnesindeki yeniden yönlendirme işlevini geçersiz kılın. - Yönlendirme işlevi içinde, yönlendirme işlevinin hangi parametrelerle çağrılması gerektiği konusunda iddialarda bulunmaya yardımcı olması için chai.assert kullanın:
// 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);
Test temizliği
Test kodunuzun en sonunda, temizleme işlevini arayın. Bu, SDK'nın başlatıldığında ayarladığı ortam değişkenlerini kaldırır ve gerçek zamanlı veritabanı DataSnapshot
veya Firestore DocumentSnapshot
oluşturmak için SDK'yı kullandıysanız oluşturulmuş olabilecek Firebase uygulamalarını siler.
test.cleanup();
Tam örnekleri inceleyin ve daha fazlasını öğrenin
Tam örnekleri Firebase GitHub deposunda inceleyebilirsiniz.
- Çevrimiçi Modda Gerçek Zamanlı Veritabanı ve HTTP İşlevlerini Test Etme
- Çevrimdışı Modda Gerçek Zamanlı Veritabanı ve HTTP İşlevlerini Test Etme
Daha fazla bilgi edinmek için firebase-functions-test
için API referansına bakın.