Cloud Functions birim testi

Bu sayfada, işletmeniz için birim testleri yazmaya yönelik en iyi uygulamalar ve araçlar açıklanmaktadır Sürekli Entegrasyon'un (CI) parçası olacak testler gibi işlevler bahsedeceğim. Firebase, testi kolaylaştırmak amacıyla Cloud Functions için Firebase Test SDK sağlar. Google npm'de firebase-functions-test olarak dağıtılır ve tamamlayıcı test SDK'sıdır Hedef: firebase-functions. Cloud Functions için Firebase Test SDK:

  • Uygun ayarları yapar ve testleriniz için gerekli ayarları yapar, örneğin şunları içerir: firebase-functions için gereken ortam değişkenlerini ayarlayıp ayarlamadan kaldırın.
  • Yalnızca belirtmeniz gereken örnek veriler ve etkinlik bağlamı oluşturur testinizle ilgili tüm alanları inceleyin.
ziyaret edin.

Test kurulumu

Hem firebase-functions-test hem de Mocha'yı yükleyin. test çerçevesini kullanabilirsiniz. Bunun için işlev klasörünüzde aşağıdaki komutları çalıştırın:

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

Sonra, işlevler klasöründe bir test klasörü ve yeni bir dosya oluşturun. kod olarak ekleyin ve index.test.js gibi bir ad verin.

Son olarak, aşağıdakini eklemek için functions/package.json öğesini değiştirin:

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

Testleri yazdıktan sonra içeride npm test komutunu çalıştırarak test edebilirsiniz. dizin oluşturun.

Cloud Functions için Firebase Test SDK başlatılıyor

firebase-functions-test iki şekilde kullanılabilir:

  1. Online mod (önerilir): Bir Firebase projesiyle etkileşim kuran testler yazın yazma, kullanıcı oluşturma vb. işlemlerin yapılmasını sağlamak için gereken ve test kodunuz sonuçları inceleyebilir. Bu, aynı zamanda diğer İşlevlerinizde kullanılan Google SDK'ları da çalışır.
  2. Çevrimdışı mod: Yan etki olmadan, ayrı ayrı ve çevrimdışı birim testleri yazın. Yani, Firebase ürünüyle (ör. veya kullanıcı oluşturma gibi) stublaştırılması gerekir. Çevrimdışı kullanılıyor Cloud Firestore veya Realtime Database kullanıyorsanız mod genellikle önerilmez işlevini kullanın.

SDK'yı online modda başlat (önerilir)

Bir test projesiyle etkileşim kuran testler yazmak istiyorsanız uygulamayı ilk kullanıma hazırlamak için gereken proje yapılandırma değerlerini sağlayın: firebase-admin ve bir hizmet hesabı anahtar dosyasının yolu.

Firebase projenizin yapılandırma değerlerini almak için:

  1. Proje ayarlarınızı şurada açın: Firebase konsolu.
  2. Uygulamalarınız bölümünde istediğiniz uygulamayı seçin.
  3. Sağ bölmede, yapılandırma dosyası indirme seçeneğini belirleyin yeniden hedefleme kampanyaları için kullanılabilir.

    Web uygulamaları için Yapılandırma'yı seçerek görüntüleyin yapılandırma değerleridir.

Anahtar dosyası oluşturmak için:

  1. Hizmet Hesapları bölmesini açın. Google Cloud konsolunda görebilirsiniz.
  2. Varsayılan App Engine hizmet hesabını seçin ve şuradaki seçenekler menüsünü kullanın: Anahtar oluştur'u seçmek için sağına dokunun.
  3. İstendiğinde anahtar türü için JSON'u seçin ve Create'i (Oluştur) 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ı başlatabilirsiniz hiçbir parametre olmadan:

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

Sahte yapılandırma değerleri

İşlevler kodunuzda functions.config() kullanıyorsanız yapılandırmayla deneme yapabilirsiniz. değerler. Ö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ızın içindeki değeri aşağıdaki şekilde 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şlev dosyanızı modülünü kullanabilirsiniz. Bunu yalnızca firebase-functions-test, yapılandırma değerleridir.

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

firebase-functions-test uygulamasını şurada başlattıysanız: çevrimdışı modda ve admin.initializeApp() kullanıyorsanız, daha önce bunu saplamanız gerekir içe aktarma:

// 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şlevleri test etme süreci aşağıdaki adımları içerir:

  1. Test etmek istediğiniz işlevi test.wrap yöntemiyle sarmalayın
  2. Test verilerini oluşturma
  3. Sarmalanmış işlevi, oluşturduğunuz test verileri ve herhangi bir etkinlikle çağırma bağlam alanlarını da kullanabilirsiniz.
  4. Davranış hakkında iddialarda bulunmak.

Önce test etmek istediğiniz işlevi sarmalayın. Diyelim ki makeUppercase adlı functions/index.js adlı katılımcının testini yapmak istiyorsunuz. Şunu yazın: functions/test/index.test.js içinde takip ediliyor

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

wrapped, çağrıldığında makeUppercase yöntemini çağıran bir işlevdir. wrapped. 2 parametre alır:

  1. data (gerekli): makeUppercase ürününe gönderilecek veriler. Doğrudan bu bu parametrenin, istediğiniz fonksiyon işleyiciye gönderilen yazdı. firebase-functions-test, özel oluşturma yöntemleri sağlar verileri veya örnek verileri içerir.
  2. eventContextOptions (isteğe bağlı): belirtmek istediğiniz etkinlik bağlamı alanları tercih edebilirsiniz. Etkinlik bağlamı, fonksiyon işleyicisini kullanabilirsiniz. Bir eventContextOptions eklemezseniz wrapped parametresi çağrılırken, etkinlik bağlamı oluşturulmaya devam ediyor bu verileri kullanabilirsiniz. Oluşturulan alanlardan bazılarını burada belirtmelisiniz. Yalnızca aşağıdaki gibi görünen alanları doldurmanız gerektiğini unutmayın: anahtar kelimeleri belirleyebilirsiniz. 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, koordineli olacaklardır. Test verilerini oluşturmanın birkaç yolu vardır.

Özel verileri kullanma

firebase-functions-test, gerekli verileri oluşturmak için çeşitli işlevlere sahiptir fonksiyonlarınızı test edin. Örneğin, test.firestore.makeDocumentSnapshot kullanın ve Firestore DocumentSnapshot oluşturun. İlk bağımsız değişken veridir ve ikinci bağımsız değişken tam referans yoludur ve isteğe bağlı üçüncü bağımsız değişken diğer özellikleri için de geçerli olur.

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

onUpdate veya onWrite işlevini test ediyorsanız biri öncesi, diğeri sonrası durumu için olmak üzere iki anlık görüntü. Daha sonra bu anlık görüntülerle Change nesnesi oluşturmak için makeChange yöntemini kullanabilir.

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

Benzer işlevler için API referansına bakın diğer tüm veri türleri için geçerli.

Örnek verileri kullanma

Testlerinizde kullanılan verileri özelleştirmeniz gerekmiyorsa firebase-functions-test, her biri için örnek veri oluşturma yöntemleri sunar işlev türünü belirtin.

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

Aşağıdaki yöntemler için API referansını inceleyin: işlev türü için örnek veriler alınıyor.

Stdubed veri kullanma (çevrimdışı mod için)

SDK'yı çevrimdışı modda başlattıysanız ve bir Cloud Firestore veya Realtime Database işlevi için saplama içeren düz bir nesne kullanmalısınız oluşturmak yerine gerçek bir DocumentSnapshot veya DataSnapshot oluşturun.

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 kökten değiştirmek için Sinon'u 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);

Onaylama

SDK'yı başlattıktan, işlevleri sarmaladıktan ve verileri oluşturduktan sonra oluşturulan verilerle sarmalanmış işlevleri çağırabilir ve onaylamalar yapabilir düşünmesi gerekir. Şu dosyalar için Chai gibi bir kitaplık kullanabilirsiniz: en iyi uygulamaları paylaşacağım.

Online modda onaylamalar yapma

Cloud Functions için Firebase Test SDK uygulamasını çevrimiçi modda başlattıysanız: veri tabanında yazma gibi istenilen işlemlerin (ör. veritabanına yazma) firebase-admin SDK'sı kullanılıyor.

Aşağıdaki örnekte 'INPUT' iddia ediliyor web sitesinin veri tabanına test projesi olabilir.

// 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 onay yapma

İşlevin beklenen dönüş değeri hakkında onay alabilirsiniz:

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

Ayrıca, Sinon casuslarını belirli yöntemlerin çağrıldığını ve beklediğiniz parametrelerle yapıldığını iddia etmek.

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 Şu koşullar geçerliyse firebase-functions-test:

  • functions.config() kullanıyorsunuz
  • İşleviniz bir Firebase projesiyle veya diğer Google API'leriyle etkileşime geçtiğinde 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 nesnesini tanımlayın. addMessage() örnek işlevini nasıl test edebileceğiniz aşağıda açıklanmıştır:

  • sendMessage() tarihinden itibaren yanıt nesnesindeki yönlendirme işlevini geçersiz kıl buna denir.
  • Yönlendirme işlevinde chai.assert parametresini kullanın. Böylece, yönlendirme işlevinin şununla çağrılmalıdır:
// 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 politika, ayarlanmadan bırakılır ilk kullanıma hazırlanırken SDK'nın ayarladığı ortam değişkenlerini ve Gerçek bir SDK oluşturmak için SDK'yı kullanmanız halinde oluşturulmuş olabilecek Firebase DataSnapshot veya Firestore DocumentSnapshot zaman veritabanı.

test.cleanup();

Tüm örnekleri inceleyin ve daha fazla bilgi edinin

Tüm örnekleri Firebase GitHub deposunda inceleyebilirsiniz.

Daha fazla bilgi edinmek için API referansına bakın firebase-functions-test için.