Bu sayfada, Sürekli Entegrasyon (CI) sisteminin parçası olabilecek testler gibi işlevlerinizle ilgili birim testleri yazmaya yönelik en iyi uygulamalar ve araçlar açıklanmaktadır. Firebase, testi kolaylaştırmak amacıyla Cloud Functions için Firebase Test SDK'sını sunar. npm'de firebase-functions-test
olarak dağıtılır ve firebase-functions
için tamamlayıcı bir test SDK'sıdır. Cloud Functions için Firebase Test SDK'sı:
- Testleriniz için uygun kurulum ve söküm işlemlerini (örneğin,
firebase-functions
için gereken ortam değişkenlerini ayarlama ve kaldırma işlemlerini gerçekleştirir.) - Yalnızca testinizle ilgili alanları belirtmenizi sağlamak için örnek veri ve etkinlik bağlamı oluşturur.
Test kurulumu
İşlevler klasörünüzde aşağıdaki komutları çalıştırarak hem firebase-functions-test
hem de Mocha'yı yükleyin:
npm install --save-dev firebase-functions-test
npm install --save-dev mocha
Daha sonra, işlevler klasörünün içinde bir test
klasörü oluşturun, test kodunuz için bu klasörün içinde yeni bir dosya oluşturun ve index.test.js
gibi bir ad verin.
Son olarak, functions/package.json
öğesini değiştirerek aşağıdakileri ekleyin:
"scripts": {
"test": "mocha --reporter spec"
}
Testleri yazdıktan sonra işlevler dizininizde npm test
komutunu çalıştırarak çalıştırabilirsiniz.
Cloud Functions için Firebase Test SDK'sını başlatma
firebase-functions-test
, iki şekilde kullanılabilir:
- Online mod (önerilir): Veritabanı yazma, kullanıcı oluşturma vb. işlemlerinin gerçekten gerçekleşmesi ve test kodunuzun sonuçları inceleyebilmesi için teste özel bir Firebase projesiyle etkileşime giren testler yazın. Bu, işlevlerinizde kullanılan diğer Google SDK'larının da çalışacağı anlamına gelir.
- Çevrimdışı mod: Yan etki olmadan, ayrı ve çevrimdışı birim testleri yazın. Diğer bir deyişle, bir Firebase ürünüyle etkileşime giren (ör. veritabanına yazma veya kullanıcı oluşturma) tüm yöntem çağrılarının saptırılması gerekir. Test kodunuzun karmaşıklığını büyük ölçüde artırdığından, Cloud Firestore veya Realtime Database işlevleriniz varsa çevrimdışı modu kullanmanız genellikle önerilmez.
SDK'yı online modda başlat (önerilir)
Bir test projesiyle etkileşimde bulunan testler yazmak isterseniz uygulamayı firebase-admin
aracılığıyla başlatmak için gereken proje yapılandırma değerlerini ve hizmet hesabı anahtar dosyasının yolunu 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 bölümünde istediğiniz uygulamayı seçin.
Sağ bölmede, Apple ve Android uygulamaları için yapılandırma dosyası indirme seçeneğini belirleyin.
Web uygulamaları için yapılandırma değerlerini görüntülemek üzere Yapılandırma'yı seçin.
Anahtar dosyası oluşturmak için:
- Google Cloud Console'un Hizmet Hesapları bölmesini açın.
- App Engine'in varsayılan hizmet hesabını seçin ve sağdaki seçenekler menüsünü kullanarak Anahtar oluştur'u seçin.
- İstendiğinde anahtar türü olarak JSON öğesini 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
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')();
Örnek yapılandırma değerleri
İşlevler kodunuzda functions.config()
kullanırsanız yapılandırma değerleriyle taklit 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ızdaki değerle şu şekilde model oluşturabilirsiniz:
// Mock functions config values
test.mockConfig({ stripe: { key: '23wr42ewr34' }});
İşlevlerinizi içe aktarma
İşlevlerinizi içe aktarmak için require
kullanarak ana işlevler dosyanızı modül olarak içe aktarın. Bunu yalnızca firebase-functions-test
uygulamasını başlattıktan ve yapılandırma değerleriyle dalga geçirdikten 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
öğesini çevrimdışı modda başlattıysanız ve işlev kodunuzda admin.initializeApp()
varsa işlevlerinizi içe aktarmadan önce kodu tespit etmeniz 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şlevlerini test etme
HTTP olmayan işlevlerin test edilmesi için süreç aşağıdaki adımları içerir:
test.wrap
yöntemiyle test etmek istediğiniz işlevi sarmalayın- Test verilerini oluşturma
- Sarmalanmış işlevi, oluşturduğunuz test verileri ve belirtmek istediğiniz etkinlik bağlamı alanlarıyla çağırın.
- Davranışla ilgili iddialarda bulunmak.
Önce test etmek istediğiniz işlevi sarmalayın. functions/index.js
ürününde test etmek istediğiniz makeUppercase
adlı bir işleviniz olduğunu varsayalım. Aşağıdakileri functions/test/index.test.js
dilinde yazın
// "Wrap" the makeUpperCase function from index.js
const myFunctions = require('../index.js');
const wrapped = test.wrap(myFunctions.makeUppercase);
wrapped
, çağrıldığında makeUppercase
işlevini çağıran bir işlevdir. wrapped
2 parametre alır:
- data (gerekli):
makeUppercase
ürününe gönderilecek veriler. Bu, doğrudan yazdığınız işlev işleyiciye gönderilen ilk parametreye karşılık gelir.firebase-functions-test
, özel veriler veya örnek veriler oluşturmaya yönelik yöntemler sunar. - eventContextOptions (isteğe bağlı): Belirtmek istediğiniz etkinlik bağlamının alanları. Etkinlik bağlamı, yazdığınız işlev işleyiciye gönderilen ikinci parametredir.
wrapped
çağırırkeneventContextOptions
parametresi eklemezseniz yine de anlamlı alanlarla bir etkinlik bağlamı 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 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 verilerini oluşturma
Sarmalanmış bir işlevin ilk parametresi, temel işlevi birlikte çağıracak 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şturmaya yönelik çeşitli işlevlere sahiptir. Örneğin, test.firestore.makeDocumentSnapshot
kullanarak bir Firestore DocumentSnapshot
oluşturun. Birinci bağımsız değişken verilerdir, ikinci bağımsız değişken ise tam referans yoludur ve anlık görüntünün belirtebileceğiniz diğer özellikleri için 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: Önceki durum için bir anlık görüntü ve sonraki durum için bir anlık görüntü. 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ürleriyle ilgili benzer işlevler için API referansı bölümüne bakın.
Örnek verileri kullanma
Testlerinizde kullanılan verileri özelleştirmeniz gerekmiyorsa firebase-functions-test
, her işlev türü için örnek veriler oluşturma yöntemleri 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üne ait örnek verileri alma yöntemleri için API referansı bölümüne bakın.
Standart 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 kola içeren düz bir nesne kullanmanız gerekir.
Aşağıdaki fonksiyon için bir ünite 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ılmıştır:
snap.val()
snap.ref.parent.child('uppercase').set(uppercase)
Test kodunda, bu kod yollarının 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);
Onaylarda bulunma
SDK'yı başlattıktan, işlevleri sarmaladıktan ve verileri oluşturduktan sonra, sarmalanmış işlevleri oluşturulan verilerle çağırabilir ve davranış hakkında iddialarda bulunabilirsiniz. Bu iddiaları yapmak için Chai gibi bir kitaplık kullanabilirsiniz.
Online modda onaylama yapma
Cloud Functions için Firebase Test SDK'sını online modda başlattıysanız firebase-admin
SDK'sını kullanarak istenen işlemlerin (veritabanı yazma gibi) gerçekleştirildiğini iddia edebilirsiniz.
Aşağıdaki örnekte, "INPUT" öğesinin test projesinin veritabanına yazıldığı iddia edilmektedir.
// 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 onaylama yapma
İşlevin beklenen döndürdüğü değer 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);
Belirli yöntemlerin çağrıldığını ve beklediğiniz parametrelerle çağrıldığını iddia etmek için Sinon casusları da kullanabilirsiniz.
HTTP işlevlerini test etme
HTTP onCall işlevlerini test etmek için arka plan işlevlerini test etme yaklaşımıyla aynı yaklaşımı kullanın.
HTTP onRequest işlevlerini test ediyorsanız aşağıdaki durumlarda firebase-functions-test
kullanmanız gerekir:
functions.config()
kullanıyorsunuz- İşleviniz bir Firebase projesiyle veya başka Google API'leriyle etkileşim halindeyse ve testlerinizde gerçek bir Firebase projesini ve bu projenin kimlik bilgilerini kullanmak istiyorsunuz.
HTTP onRequest işlevi iki parametre alır: istek nesnesi ve yanıt nesnesi. addMessage()
örnek işlevini şu şekilde test edebilirsiniz:
- Yanıt nesnesindeki yönlendirme işlevini,
sendMessage()
çağırdığından geçersiz kılın. - Yönlendirme işlevinde, yönlendirme işlevinin hangi parametrelerle çağrılması gerektiğiyle ilgili onaylar konusunda yardımcı olması için chai.assert kodunu 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);
Temizleme testi
Test kodunuzun en sonundaki temizleme işlevini çağırın. Bu işlem, SDK'nın ilk kullanıma hazırlanırken ayarladığı ortam değişkenlerini iptal eder ve SDK'yı kullanarak gerçek zamanlı veritabanı DataSnapshot
veya Firestore DocumentSnapshot
oluşturmak için kullanmış olabileceğiniz Firebase uygulamalarını siler.
test.cleanup();
Örneklerin tamamını inceleyin ve daha fazla bilgi edinin
Firebase GitHub deposunda örneklerin tamamını inceleyebilirsiniz.
- Gerçek Zamanlı Veritabanı ve HTTP İşlevlerini Online Modda Test Etme
- Gerçek Zamanlı Veritabanı ve HTTP İşlevlerini Çevrimdışı Modda Test Etme
Daha fazla bilgi edinmek için firebase-functions-test
API referansına bakın.