Bu sayfada, işlevleriniz için birim testleri yazmayla ilgili en iyi uygulamalar ve araçlar (ör. sürekli entegrasyon (CI) sisteminin bir parçası olacak testler) açıklanmaktadır. Firebase, testleri kolaylaştırmak için Cloud Functions için Firebase Test SDK sağlar. npm'de firebase-functions-test
olarak dağıtılan bu SDK, firebase-functions
için tamamlayıcı bir test SDK'sıdır. Cloud Functions için Firebase Test SDK:
firebase-functions
tarafından ihtiyaç duyulan ortam değişkenlerini ayarlama ve kaldırma gibi testleriniz için uygun kurulum ve kaldırma işlemlerini gerçekleştirir.- Yalnızca testinizle alakalı alanları belirtmeniz gerektiği için örnek veriler ve etkinlik bağlamı oluşturur.
Test kurulumu
Functions klasörünüzde aşağıdaki komutları çalıştırarak hem firebase-functions-test
hem de bir test çerçevesi olan Mocha'yı yükleyin:
npm install --save-dev firebase-functions-test
npm install --save-dev mocha
Ardından, functions 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 dosyayı index.test.js
gibi bir adla adlandırın.
Son olarak, functions/package.json
dosyasını aşağıdakileri ekleyecek şekilde değiştirin:
"scripts": {
"test": "mocha --reporter spec"
}
Testleri yazdıktan sonra, işlev dizininizde npm test
çalıştırarak testleri çalıştırabilirsiniz.
Cloud Functions için Firebase Test SDK başlatılıyor
firebase-functions-test
öğesini kullanmanın iki yolu vardır:
- Online mod (önerilir): Testlere özel bir Firebase projesiyle etkileşime geçen testler yazın. Böylece veritabanı yazma, kullanıcı oluşturma vb. işlemler gerçekten gerçekleşir ve test kodunuz sonuçları inceleyebilir. Bu, işlevlerinizde kullanılan diğer Google SDK'larının da çalışacağı anlamına gelir.
- Çevrimdışı mod: Yan etkisi olmayan, birbirinden bağımsız ve çevrimdışı birim testleri yazın. Bu, bir Firebase ürünüyle etkileşime geçen tüm yöntem çağrılarının (ör. veritabanına yazma veya kullanıcı oluşturma) stub'e dönüştürülmesi gerektiği anlamına gelir. Cloud Firestore veya Realtime Database işlevleriniz varsa test kodunuzun karmaşıklığını büyük ölçüde artırdığı için çevrimdışı modu kullanmanızı genellikle önermeyiz.
SDK'yı online modda başlatma (önerilen)
Bir test projesiyle etkileşime geçen testler yazmak istiyorsanız firebase-admin
üzerinden uygulamayı başlatmak için gereken proje yapılandırma değerlerini ve bir 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 konsolunun Hizmet Hesapları bölmesini açın.
- App Engine 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ü 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şlatma
Tamamen çevrimdışı testler yazmak istiyorsanız SDK'yı herhangi bir parametre olmadan başlatabilirsiniz:
// At the top of test/index.test.js
const test = require('firebase-functions-test')();
Yapılandırma değerlerini taklit etme
İşlev kodunuzda functions.config()
kullanıyorsanız yapılandırma değerlerini taklit edebilirsiniz. Örneğin, functions/index.js
aşağıdaki kodu içeriyorsa:
const functions = require('firebase-functions/v1');
const key = functions.config().stripe.key;
Ardından, test dosyanızdaki değeri aşağıdaki gibi taklit edebilirsiniz:
// Mock functions config values
test.mockConfig({ stripe: { key: '23wr42ewr34' }});
İşlevlerinizi içe aktarma
İşlevlerinizi içe aktarmak için ana işlev dosyanızı modül olarak içe aktarmak üzere require
simgesini kullanın. Bunu yalnızca firebase-functions-test
'ı başlattıktan ve yapılandırma değerlerini taklit 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 başlattıysanız ve işlev kodunuzda admin.initializeApp()
varsa işlevlerinizi içe aktarmadan önce bu işlevi stub'a dönüştürmeniz 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 dışı işlevleri test etme süreci aşağıdaki adımları içerir:
- Test etmek istediğiniz işlevi
test.wrap
yöntemiyle sarmalayın - Test verileri oluşturma
- Oluşturduğunuz test verileriyle ve belirtmek istediğiniz tüm etkinlik bağlamı alanlarıyla sarmalanmış işlevi çağırın.
- Davranışla ilgili iddialarda bulunun.
Öncelikle test etmek istediğiniz işlevi sarmalayın. functions/index.js
adlı bir işleviniz olduğunu ve bu işlevi test etmek istediğinizi varsayalım.makeUppercase
Aşağıdakileri functions/test/index.test.js
alanına 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 (zorunlu):
makeUppercase
adresine gönderilecek veriler. Bu, yazdığınız işlev işleyiciye gönderilen ilk parametreye doğrudan karşılık gelir.firebase-functions-test
, özel veri veya örnek veri oluşturma yöntemleri sağlar. - 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
işlevini çağırırkeneventContextOptions
parametresi eklemezseniz yine de anlamlı alanlar içeren bir etkinlik bağlamı oluşturulur. Oluşturulan alanların bazılarını burada belirterek geçersiz kılabilirsiniz. Yalnızca geçersiz kılmak istediğiniz alanları eklemeniz gerektiğini unutmayın. Üzerine yazmadığı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 verileri oluşturma
Sarmalanmış bir işlevin ilk parametresi, temel işlevi çağırırken kullanılacak test verileridir. Test verileri 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, Firestore DocumentSnapshot
oluşturmak için test.firestore.makeDocumentSnapshot
seçeneğini kullanın. İlk bağımsız değişken veriler, ikinci bağımsız değişken tam referans yoludur. Ayrıca, belirleyebileceğiniz anlık görüntünün 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 biri önceki durum, diğeri sonraki durum için olmak üzere iki anlık görüntü oluşturmanız gerekir. 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 işlev türü için örnek veri 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ü için örnek veri alma yöntemlerine API referansı bölümünden ulaşabilirsiniz.
Kaba veri 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, stubs içeren basit bir nesne kullanmanız gerekir.
Aşağıdaki işlev 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ğı basit bir nesne oluşturun ve yöntemlerin yerine 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);
İddialarda 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ışla ilgili iddialarda bulunabilirsiniz. Bu iddiaları yapmak için Chai gibi bir kitaplık kullanabilirsiniz.
Online modda beyanlarda bulunma
Cloud Functions için Firebase Test SDK'ü online modda başlattıysanız firebase-admin
SDK'sını kullanarak istenen işlemlerin (ör. veritabanı yazma) gerçekleştiğini doğrulayabilirsiniz.
Aşağıdaki örnekte, "INPUT" değerinin 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 beyanlarda bulunma
İşlevin beklenen döndürülen 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);
Belirli yöntemlerin çağrıldığını ve beklediğiniz parametrelerle çağrıldığını doğrulamak için Sinon casuslarını da kullanabilirsiniz.
HTTP işlevlerini test etme
HTTP onCall işlevlerini test etmek için arka plan işlevlerini test etme ile 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ıyorsanız- İşleviniz bir Firebase projesi veya diğer Google API'leriyle etkileşim kuruyorsa ve testleriniz için gerçek bir Firebase projesi ve kimlik bilgilerini kullanmak istiyorsanız
HTTP onRequest işlevi iki parametre alır: istek nesnesi ve yanıt nesnesi. addMessage()
örnek işlevini aşağıdaki gibi test edebilirsiniz:
sendMessage()
bu işlevi çağırdığından, yanıt nesnesinde yönlendirme işlevini geçersiz kılın.- Yönlendirme işlevinin hangi parametrelerle çağrılacağı hakkında iddialarda bulunmak için yönlendirme işlevi içinde chai.assert'i 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 çağırın. Bu işlem, SDK'nın başlatılırken ayarlayıp kaydettiği 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();
Eksiksiz örnekleri inceleyin ve daha fazla bilgi edinin
Örneklerin tamamını Firebase GitHub deposunda inceleyebilirsiniz.
- Realtime Database ve HTTP İşlevlerini Online Modda Test Etme
- Realtime Database ve HTTP işlevlerini çevrimdışı modda test etme
Daha fazla bilgi için firebase-functions-test
ile ilgili API referansına bakın.