Чтобы настроить тестовую среду для облачной функции, которая позволяет вам моделировать чтение / запись и настроить тестовые данные, вы должны сделать следующее.
Предпосылки
Я предполагаю, что на данный момент у вас настроен проект firebase с папкой функций и index.js
. Позже тесты будут в папке functions/test
. Если у вас нет настройки проекта, используйте firebase init
для настройки проекта.
Зависимости установки
Сначала добавьте / установите следующие зависимости: mocha
, @firebase/testing
, firebase-functions-test
, firebase-functions
, firebase-admin
, firebase-tools
Инициализация и запуск эмуляторов
- База данных инициализации
firebase setup:emulators:database
- Инициативный пожарный магазин
firebase setup:emulators:firestore
- Запустите эмуляторы с
firebase emulators:start
На данный момент у вас должны быть запущены эмуляторы базы данных и firestore. Теперь мы можем читать / писать в него.
index.js для примера облачной функции
// functions/index.js
const fbInit = require("./ainitFb");
let admin = fbInit.getAdmin();
const functions = require("firebase-functions");
exports.addCrewMemeber = functions.firestore
.document("characters/{characterId}")
.onCreate(async (characterSnap, context) => {
const heartOfGold = admin
.firestore()
.collection("spaceShip")
.doc("Heart-of-Gold");
const heartData = (await heartOfGold.get()).data();
await heartOfGold.set({
crew: [...heartData.crew, context.params.characterId],
crewCount: heartData.crewCount + 1,
});
// Update the characters to be in space
return characterSnap.ref.update({ inSpace: true });
});
Вы видите, что я не импортирую admin
напрямую из firebase-admin
. Но вместо require("./ainitFb")
. Я делаю это потому, что нам нужно заглушить администратора, с нашим новым администратором, указывающим на эмулятор.
Создайте модуль firebase-admin, чтобы разрешить насмешку над firebase-admin
// functions/aniniFb.js
exports.initializeMockApp = function(mock) {
admin = mock;
};
exports.initializeApp = function() {
if (!admin) {
admin = require("firebase-admin");
admin.initializeApp();
}
};
exports.getAdmin = function() {
return global.admin;
};
exports.admin = (inneradmin => {
return inneradmin;
})(
global.admin,
);
тестовый файл мокко test.js
// functions/test/test.js
const assert = require("assert");
const projectId = "dummy";
const firebase = require("@firebase/testing");
const admin = firebase.initializeAdminApp({ projectId, databaseName: projectId });
const test = require("firebase-functions-test")({ projectId, databaseName: projectId });
const fbInit = require("../ainitFb");
// this will inject the mocked admin
fbInit.initializeMockApp(admin);
// load this only after you ran 'initializeMockApp'
const myFunctions = require("../index.js");
// Create a simple document snapshot
// I did that because currently using test.firestore.makeDocumentSnapshot() is not working
// For some reasons it says the firebase db can't be reached, while it's actually running
function makeDocumentSnapshot(data, ref) {
return {
id: ref.id,
data: () => data,
ref,
};
}
beforeEach(async function() {
// we remove the timeout as the default is 2sec which is often too short to also start the emulators
this.timeout(0);
// Clear the database between tests
await firebase.clearFirestoreData({ projectId });
});
it("Add Crew Members", async function() {
this.timeout(0);
const heartOfGold = admin
.firestore()
.collection("spaceShip")
.doc("Heart-of-Gold");
const trillianRef = admin
.firestore()
.collection("characters")
.doc("Trillian");
// init crew members of the Heart of Gold
await heartOfGold.set({
crew: [],
crewCount: 0,
});
// save the character Trillian to the DB
const trillianData = { name: "Trillian", inSpace: false };
await trillianRef.set(trillianData);
// Add Trillian to the Heart of Gold
const trillianSnap = makeDocumentSnapshot(trillianData, trillianRef);
const addCrewMemeber = test.wrap(myFunctions.addCrewMemeber);
await addCrewMemeber(trillianSnap, { params: { characterId: "Trillian" } });
// check if the crew size has change
const heart = await heartOfGold.get();
const trillian = await trillianRef.get();
// at this point the Heart of Gold has one crew member and trillian is in space
assert.deepStrictEqual(heart.data().crewCount, 1, "Crew Members");
assert.deepStrictEqual(trillian.data().inSpace, true, "In Space");
});
запустить тест
Для запуска теста мы можем просто запустить mocha с mocha
, если он установлен глобально, или yarn mocha
, если локально установлен через yarn
.
Если все это работает, вы должны увидеть следующий вывод
√ Add Crew Members (189ms)
1 passing (223ms)
Запуск его в системе CI
Чтобы запустить тот же код в системе CI, вы должны убедиться, что эмуляторы запущены, прежде чем вы сможете запустить тест. Это можно просто сделать с помощью команды firebase emulators:exec
. Например, вот так firebase emulators:exec "mocha --exit"
. Не забудьте --exit
, иначе мокко может не закончиться, и ваш CI-runner не остановится. Кроме того, если у вас возникают проблемы с запуском / запуском теста или эмулятора, возможно, вы инициализировали их в неправильной папке. Я пользуюсь этим yarn firebase emulators:exec --only database,firestore "yarn mocha functions --exit"
, чтобы исправить проблему, при которой мокко выглядит не в той папке.