Создание модульных тестов

Firebase Local Emulator Suite упрощает полную проверку функций и поведения вашего приложения. Это также отличный инструмент для проверки конфигураций Firebase Security Rules . Используйте Firebase Emulators для запуска и автоматизации модульных тестов в локальной среде. Методы, описанные в этом документе, должны помочь вам при создании и автоматизации модульных тестов для вашего приложения, которые проверяют ваши Rules .

Если вы еще этого не сделали, настройте эмуляторы Firebase .

Перед запуском эмулятора

Прежде чем начать использовать эмулятор, имейте в виду следующее:

  • Эмулятор изначально загрузит правила, указанные в поле firestore.rules или storage.rules вашего файла firebase.json . Если файл не существует и вы не используете метод loadFirestoreRules или loadStorageRules , как описано ниже, эмулятор рассматривает все проекты как имеющие открытые правила.
  • Хотя большинство Firebase SDK работают с эмуляторами напрямую, только библиотека @firebase/rules-unit-testing поддерживает фиктивную auth в правилах безопасности, что значительно упрощает модульные тесты. Кроме того, библиотека поддерживает несколько функций, специфичных для эмулятора, например очистку всех данных, как указано ниже.
  • Эмуляторы также будут принимать производственные токены Firebase Auth, предоставляемые через клиентские SDK, и соответствующим образом оценивать правила, что позволит подключать ваше приложение напрямую к эмуляторам при интеграционных и ручных тестах.

Различия между эмуляторами баз данных и производством

  • Вам не нужно явно создавать экземпляр базы данных. Эмулятор автоматически создаст любой экземпляр базы данных, к которому осуществляется доступ.
  • Каждая новая база данных запускается с закрытыми правилами, поэтому пользователи, не имеющие прав администратора, не смогут читать или писать.
  • Каждая эмулируемая база данных применяет ограничения и квоты плана Spark (в частности, это ограничение каждого экземпляра 100 одновременными подключениями).
  • Любая база данных примет строку "owner" в качестве токена аутентификации администратора.
  • В настоящее время эмуляторы не имеют рабочих взаимодействий с другими продуктами Firebase. В частности, обычный поток аутентификации Firebase не работает. Вместо этого вы можете использовать метод initializeTestApp() в библиотеке rules-unit-testing , который принимает поле auth . Объект Firebase, созданный с помощью этого метода, ведет себя так, как будто он успешно прошел аутентификацию как любая предоставленная вами сущность. Если вы передадите null , он будет вести себя как неаутентифицированный пользователь (например, правила auth != null не пройдут).

Взаимодействие с эмулятором Realtime Database

Экземпляр Realtime Database доступен на поддомене firebaseio.com , а доступ к API REST можно получить следующим образом:

https://<database_name>.firebaseio.com/path/to/my/data.json

Эмулятор работает локально и доступен по адресу localhost:9000 . Для взаимодействия с определенным экземпляром базы данных вам придется использовать параметр запроса ns , чтобы указать имя базы данных.

http://localhost:9000/path/to/my/data.json?ns=<database_name>

Запуск локальных модульных тестов с помощью JavaScript SDK версии 9

Firebase распространяет библиотеку модульного тестирования Security Rules как с версией 9 JavaScript SDK, так и с версией 8 SDK. API библиотеки существенно различаются. Мы рекомендуем библиотеку тестирования v9, которая более оптимизирована и требует меньше настроек для подключения к эмуляторам, что позволяет безопасно избегать случайного использования производственных ресурсов. Для обратной совместимости мы продолжаем предоставлять библиотеку тестирования v8 .

Используйте модуль @firebase/rules-unit-testing для взаимодействия с эмулятором, который работает локально. Если вы получаете тайм-ауты или ошибки ECONNREFUSED , дважды проверьте, что эмулятор действительно запущен.

Мы настоятельно рекомендуем использовать последнюю версию Node.js, чтобы вы могли использовать нотацию async/await . Почти все поведение, которое вы, возможно, захотите протестировать, включает асинхронные функции, а модуль тестирования предназначен для работы с кодом на основе Promise.

Библиотека модульного тестирования v9 Rules всегда знает об эмуляторах и никогда не затрагивает ваши производственные ресурсы.

Вы импортируете библиотеку с помощью операторов импорта v9 modular. Например:

import {
  assertFails,
  assertSucceeds,
  initializeTestEnvironment
} from "@firebase/rules-unit-testing"

// Use `const { … } = require("@firebase/rules-unit-testing")` if imports are not supported
// Or we suggest `const testing = require("@firebase/rules-unit-testing")` if necessary.

После импорта реализация модульных тестов включает в себя:

  • Создание и настройка RulesTestEnvironment с помощью вызова initializeTestEnvironment .
  • Настройка тестовых данных без запуска Rules с использованием удобного метода, который позволяет временно их обойти, RulesTestEnvironment.withSecurityRulesDisabled .
  • Настройка тестового набора и перехватов до/после для каждого теста с вызовами для очистки тестовых данных и среды, например RulesTestEnvironment.cleanup() или RulesTestEnvironment.clearFirestore() .
  • Реализация тестовых случаев, имитирующих состояния аутентификации, с использованием RulesTestEnvironment.authenticatedContext и RulesTestEnvironment.unauthenticatedContext .

Общие методы и функции полезности

См. также методы тестирования, специфичные для эмуляторов, с использованием модульного API .

initializeTestEnvironment() => RulesTestEnvironment

Эта функция инициализирует тестовую среду для тестирования модулей правил. Сначала вызовите эту функцию для настройки теста. Для успешного выполнения требуется, чтобы были запущены эмуляторы.

Функция принимает необязательный объект, определяющий TestEnvironmentConfig , который может состоять из идентификатора проекта и параметров конфигурации эмулятора.

let testEnv = await initializeTestEnvironment({
  projectId: "demo-project-1234",
  firestore: {
    rules: fs.readFileSync("firestore.rules", "utf8"),
  },
});

RulesTestEnvironment.authenticatedContext({ user_id: string, tokenOptions?: TokenOptions }) => RulesTestContext

Этот метод создает RulesTestContext , который ведет себя как аутентифицированный пользователь Authentication . Запросы, созданные через возвращенный контекст, будут иметь прикрепленный фиктивный токен Authentication . При желании передайте объект, определяющий пользовательские утверждения или переопределения для полезных нагрузок токена Authentication .

Используйте возвращаемый объект контекста теста в своих тестах для доступа к любым настроенным экземплярам эмулятора, включая настроенные с помощью initializeTestEnvironment .

// Assuming a Firestore app and the Firestore emulator for this example
import { setDoc } from "firebase/firestore";

const alice = testEnv.authenticatedContext("alice", {  });
// Use the Firestore instance associated with this context
await assertSucceeds(setDoc(alice.firestore(), '/users/alice'), { ... });

RulesTestEnvironment.unauthenticatedContext() => RulesTestContext

Этот метод создает RulesTestContext , который ведет себя как клиент, не вошедший в систему через Authentication . Запросы, созданные через возвращаемый контекст, не будут иметь прикрепленных токенов Firebase Auth.

Используйте возвращаемый объект контекста теста в своих тестах для доступа к любым настроенным экземплярам эмулятора, включая настроенные с помощью initializeTestEnvironment .

// Assuming a Cloud Storage app and the Storage emulator for this example
import { getStorage, ref, deleteObject } from "firebase/storage";

const alice = testEnv.unauthenticatedContext();

// Use the Cloud Storage instance associated with this context
const desertRef = ref(alice.storage(), 'images/desert.jpg');
await assertSucceeds(deleteObject(desertRef));

RulesTestEnvironment.withSecurityRulesDisabled()

Запустите функцию настройки теста с контекстом, который ведет себя так, как будто правила безопасности отключены.

Этот метод принимает функцию обратного вызова, которая принимает контекст Security-Rules-bypassing и возвращает обещание. Контекст будет уничтожен, как только обещание будет разрешено/отклонено.

RulesTestEnvironment.cleanup()

Этот метод уничтожает все RulesTestContexts , созданные в тестовой среде, и очищает базовые ресурсы, обеспечивая чистый выход.

Этот метод никак не изменяет состояние эмуляторов. Для сброса данных между тестами используйте метод очистки данных, специфичный для эмулятора приложения.

assertSucceeds(pr: Promise<any>)) => Promise<any>

Это функция полезности тестового случая.

Функция утверждает, что предоставленное Promise, оборачивающее операцию эмулятора, будет разрешено без нарушений правил безопасности.

await assertSucceeds(setDoc(alice.firestore(), '/users/alice'), { ... });

assertFails(pr: Promise<any>)) => Promise<any>

Это функция полезности тестового случая.

Функция утверждает, что предоставленное Promise, оборачивающее операцию эмулятора, будет отклонено из-за нарушения правил безопасности.

await assertFails(setDoc(alice.firestore(), '/users/bob'), { ... });

Методы, специфичные для эмулятора

Также ознакомьтесь с общими методами тестирования и вспомогательными функциями, использующими модульный API .

Cloud Firestore

Cloud Firestore

RulesTestEnvironment.clearFirestore() => Promise<void>

Этот метод очищает данные в базе данных Firestore, принадлежащие projectId настроенному для эмулятора Firestore.

RulesTestContext.firestore(settings?: Firestore.FirestoreSettings) => Firestore;

Этот метод получает экземпляр Firestore для этого тестового контекста. Возвращенный экземпляр Firebase JS Client SDK может использоваться с API клиентского SDK (модульный v9 или совместимый с v9).

Realtime Database

Realtime Database

RulesTestEnvironment.clearDatabase() => Promise<void>

Этот метод очищает данные в Realtime Database , принадлежащей projectId настроенному для эмулятора Realtime Database .

RulesTestContext.database(databaseURL?: Firestore.FirestoreSettings) => Firestore;

Получить экземпляр Realtime Database для этого тестового контекста. Возвращенный экземпляр Firebase JS Client SDK может использоваться с API-интерфейсами клиентского SDK (модульными или с пространством имен, версии 9 или выше). Метод принимает URL-адрес экземпляра Realtime Database. Если указано, возвращает экземпляр для эмулированной версии пространства имен с параметрами, извлеченными из URL-адреса.

Cloud Storage

Cloud Storage

RulesTestEnvironment.clearStorage() => Promise<void>

Этот метод очищает объекты и метаданные в сегментах хранения, принадлежащих projectId , настроенному для эмулятора Cloud Storage .

RulesTestContext.storage(bucketUrl?: string) => Firebase Storage;

Этот метод возвращает экземпляр Storage, настроенный для подключения к эмулятору. Метод принимает URL-адрес gs:// к Firebase Storage Bucket для тестирования. Если указано, возвращает экземпляр Storage для эмулированной версии имени bucket.

Запуск локальных модульных тестов с помощью JavaScript SDK v8

Выберите продукт, чтобы увидеть методы, используемые Firebase Test SDK для взаимодействия с эмулятором.

Cloud Firestore

initializeTestApp({ projectId: string, auth: Object }) => FirebaseApp

Этот метод возвращает инициализированное приложение Firebase, соответствующее идентификатору проекта и переменной auth, указанным в параметрах. Используйте это для создания приложения, аутентифицированного как определенный пользователь, для использования в тестах.

firebase.initializeTestApp({
  projectId: "my-test-project",
  auth: { uid: "alice", email: "alice@example.com" }
});

initializeAdminApp({ projectId: string }) => FirebaseApp

Этот метод возвращает инициализированное административное приложение Firebase. Это приложение обходит правила безопасности при выполнении чтения и записи. Используйте это, чтобы создать приложение, аутентифицированное как администратор, для установки состояния для тестов.

firebase.initializeAdminApp({ projectId: "my-test-project" });
    

apps() => [FirebaseApp] Этот метод возвращает все инициализированные в данный момент тестовые и административные приложения. Используйте это для очистки приложений между или после тестов.

Promise.all(firebase.apps().map(app => app.delete()))

loadFirestoreRules({ projectId: string, rules: Object }) => Promise

Этот метод отправляет правила в локально работающую базу данных. Он принимает объект, который определяет правила как строку. Используйте этот метод для установки правил вашей базы данных.

firebase.loadFirestoreRules({
  projectId: "my-test-project",
  rules: fs.readFileSync("/path/to/firestore.rules", "utf8")
});
    

assertFails(pr: Promise) => Promise

Этот метод возвращает обещание, которое отклоняется, если ввод успешен, или успешно, если ввод отклонен. Используйте это, чтобы утверждать, если чтение или запись в базу данных не удались.

firebase.assertFails(app.firestore().collection("private").doc("super-secret-document").get());
    

assertSucceeds(pr: Promise) => Promise

Этот метод возвращает обещание, которое выполняется, если ввод успешен, и отклоняется, если ввод отклонен. Используйте это, чтобы подтвердить, успешно ли выполнено чтение или запись в базу данных.

firebase.assertSucceeds(app.firestore().collection("public").doc("test-document").get());
    

clearFirestoreData({ projectId: string }) => Promise

Этот метод очищает все данные, связанные с определенным проектом в локально запущенном экземпляре Firestore. Используйте этот метод для очистки после тестов.

firebase.clearFirestoreData({
  projectId: "my-test-project"
});
   

Realtime Database

Realtime Database

initializeTestApp({ databaseName: string, auth: Object }) => FirebaseApp

Используйте это для создания приложения, аутентифицированного как определенный пользователь, для использования в тестах.

Возвращает инициализированное приложение Firebase, соответствующее имени базы данных и переопределению переменной аутентификации, указанным в параметрах.

firebase.initializeTestApp({
  databaseName: "my-database",
  auth: { uid: "alice" }
});

initializeAdminApp({ databaseName: string }) => FirebaseApp

Используйте это, чтобы создать приложение, аутентифицированное как администратор, для настройки состояния для тестов.

Возвращает инициализированное административное приложение Firebase, соответствующее имени базы данных, указанному в параметрах. Это приложение обходит правила безопасности при чтении и записи в базу данных.

firebase.initializeAdminApp({ databaseName: "my-database" });

loadDatabaseRules({ databaseName: string, rules: Object }) => Promise

Используйте это для установки правил вашей базы данных.

Отправляет правила в локально работающую базу данных. Принимает объект параметров, который указывает ваши "databaseName" и ваши "правила" как строки.

firebase
      .loadDatabaseRules({
        databaseName: "my-database",
        rules: "{'rules': {'.read': false, '.write': false}}"
      });

apps() => [FirebaseApp]

Возвращает все инициализированные в данный момент тестовые и административные приложения.

Используйте это для очистки приложений между или после тестов (обратите внимание, что инициализированные приложения с активными прослушивателями предотвращают завершение работы JavaScript):

 Promise.all(firebase.apps().map(app => app.delete()))

assertFails(pr: Promise) => Promise

Возвращает обещание, которое отклоняется, если ввод успешен, и выполняется успешно, если ввод отклонен.

Используйте это, чтобы подтвердить, что чтение или запись в базу данных не удалась:

firebase.assertFails(app.database().ref("secret").once("value"));

assertSucceeds(pr: Promise) => Promise

Возвращает обещание, которое выполняется, если ввод успешен, и отклоняется, если ввод отклонен.

Используйте это, чтобы подтвердить, что чтение или запись в базу данных выполнены успешно:

firebase.assertSucceeds(app.database().ref("public").once("value"));

Cloud Storage

Cloud Storage

initializeTestApp({ storageBucket: string, auth: Object }) => FirebaseApp

Используйте это для создания приложения, аутентифицированного как определенный пользователь, для использования в тестах.

Возвращает инициализированное приложение Firebase, соответствующее имени контейнера хранилища и переопределению переменной аутентификации, указанным в параметрах.

firebase.initializeTestApp({
  storageBucket: "my-bucket",
  auth: { uid: "alice" }
});

initializeAdminApp({ storageBucket: string }) => FirebaseApp

Используйте это, чтобы создать приложение, аутентифицированное как администратор, для настройки состояния для тестов.

Возвращает инициализированное административное приложение Firebase, соответствующее имени контейнера хранилища, указанному в параметрах. Это приложение обходит правила безопасности при чтении и записи в контейнер.

firebase.initializeAdminApp({ storageBucket: "my-bucket" });

loadStorageRules({ storageBucket: string, rules: Object }) => Promise

Используйте это, чтобы задать правила для вашего хранилища.

Отправляет правила в локально управляемые контейнеры хранилища. Принимает объект параметров, который указывает ваш "storageBucket" и ваши "rules" как строки.

firebase
      .loadStorageRules({
        storageBucket: "my-bucket",
        rules: fs.readFileSync("/path/to/storage.rules", "utf8")
      });

apps() => [FirebaseApp]

Возвращает все инициализированные в данный момент тестовые и административные приложения.

Используйте это для очистки приложений между или после тестов (обратите внимание, что инициализированные приложения с активными прослушивателями предотвращают завершение работы JavaScript):

 Promise.all(firebase.apps().map(app => app.delete()))

assertFails(pr: Promise) => Promise

Возвращает обещание, которое отклоняется, если ввод успешен, и выполняется успешно, если ввод отклонен.

Используйте это, чтобы подтвердить, что чтение или запись в контейнер хранилища завершились неудачей:

firebase.assertFails(app.storage().ref("letters/private.doc").getMetadata());

assertSucceeds(pr: Promise) => Promise

Возвращает обещание, которое выполняется, если ввод успешен, и отклоняется, если ввод отклонен.

Используйте это, чтобы подтвердить, что чтение или запись в контейнер хранилища выполнены успешно:

firebase.assertFails(app.storage().ref("images/cat.png").getMetadata());

API библиотеки RUT для JS SDK v8

Выберите продукт, чтобы увидеть методы, используемые Firebase Test SDK для взаимодействия с эмулятором.

Cloud Firestore

Cloud Firestore

initializeTestApp({ projectId: string, auth: Object }) => FirebaseApp

Этот метод возвращает инициализированное приложение Firebase, соответствующее идентификатору проекта и переменной auth, указанным в параметрах. Используйте это для создания приложения, аутентифицированного как определенный пользователь, для использования в тестах.

firebase.initializeTestApp({
  projectId: "my-test-project",
  auth: { uid: "alice", email: "alice@example.com" }
});

initializeAdminApp({ projectId: string }) => FirebaseApp

Этот метод возвращает инициализированное административное приложение Firebase. Это приложение обходит правила безопасности при выполнении чтения и записи. Используйте это, чтобы создать приложение, аутентифицированное как администратор, для установки состояния для тестов.

firebase.initializeAdminApp({ projectId: "my-test-project" });
    

apps() => [FirebaseApp] Этот метод возвращает все инициализированные в данный момент тестовые и административные приложения. Используйте это для очистки приложений между или после тестов.

Promise.all(firebase.apps().map(app => app.delete()))

loadFirestoreRules({ projectId: string, rules: Object }) => Promise

Этот метод отправляет правила в локально работающую базу данных. Он принимает объект, который определяет правила как строку. Используйте этот метод для установки правил вашей базы данных.

firebase.loadFirestoreRules({
  projectId: "my-test-project",
  rules: fs.readFileSync("/path/to/firestore.rules", "utf8")
});
    

assertFails(pr: Promise) => Promise

Этот метод возвращает обещание, которое отклоняется, если ввод успешен, или успешно, если ввод отклонен. Используйте это, чтобы утверждать, если чтение или запись в базу данных не удались.

firebase.assertFails(app.firestore().collection("private").doc("super-secret-document").get());
    

assertSucceeds(pr: Promise) => Promise

Этот метод возвращает обещание, которое выполняется, если ввод успешен, и отклоняется, если ввод отклонен. Используйте это, чтобы подтвердить, успешно ли выполнено чтение или запись в базу данных.

firebase.assertSucceeds(app.firestore().collection("public").doc("test-document").get());
    

clearFirestoreData({ projectId: string }) => Promise

Этот метод очищает все данные, связанные с определенным проектом в локально запущенном экземпляре Firestore. Используйте этот метод для очистки после тестов.

firebase.clearFirestoreData({
  projectId: "my-test-project"
});
   

Realtime Database

Realtime Database

initializeTestApp({ databaseName: string, auth: Object }) => FirebaseApp

Используйте это для создания приложения, аутентифицированного как определенный пользователь, для использования в тестах.

Возвращает инициализированное приложение Firebase, соответствующее имени базы данных и переопределению переменной аутентификации, указанным в параметрах.

firebase.initializeTestApp({
  databaseName: "my-database",
  auth: { uid: "alice" }
});

initializeAdminApp({ databaseName: string }) => FirebaseApp

Используйте это, чтобы создать приложение, аутентифицированное как администратор, для настройки состояния для тестов.

Возвращает инициализированное административное приложение Firebase, соответствующее имени базы данных, указанному в параметрах. Это приложение обходит правила безопасности при чтении и записи в базу данных.

firebase.initializeAdminApp({ databaseName: "my-database" });

loadDatabaseRules({ databaseName: string, rules: Object }) => Promise

Используйте это для установки правил вашей базы данных.

Отправляет правила в локально работающую базу данных. Принимает объект параметров, который указывает ваши "databaseName" и ваши "правила" как строки.

firebase
      .loadDatabaseRules({
        databaseName: "my-database",
        rules: "{'rules': {'.read': false, '.write': false}}"
      });

apps() => [FirebaseApp]

Возвращает все инициализированные в данный момент тестовые и административные приложения.

Используйте это для очистки приложений между или после тестов (обратите внимание, что инициализированные приложения с активными прослушивателями предотвращают завершение работы JavaScript):

 Promise.all(firebase.apps().map(app => app.delete()))

assertFails(pr: Promise) => Promise

Возвращает обещание, которое отклоняется, если ввод успешен, и выполняется успешно, если ввод отклонен.

Используйте это, чтобы подтвердить, что чтение или запись в базу данных не удалась:

firebase.assertFails(app.database().ref("secret").once("value"));

assertSucceeds(pr: Promise) => Promise

Возвращает обещание, которое выполняется, если ввод успешен, и отклоняется, если ввод отклонен.

Используйте это, чтобы подтвердить, что чтение или запись в базу данных выполнены успешно:

firebase.assertSucceeds(app.database().ref("public").once("value"));

Cloud Storage

Cloud Storage

initializeTestApp({ storageBucket: string, auth: Object }) => FirebaseApp

Используйте это для создания приложения, аутентифицированного как определенный пользователь, для использования в тестах.

Возвращает инициализированное приложение Firebase, соответствующее имени контейнера хранилища и переопределению переменной аутентификации, указанным в параметрах.

firebase.initializeTestApp({
  storageBucket: "my-bucket",
  auth: { uid: "alice" }
});

initializeAdminApp({ storageBucket: string }) => FirebaseApp

Используйте это, чтобы создать приложение, аутентифицированное как администратор, для настройки состояния для тестов.

Возвращает инициализированное административное приложение Firebase, соответствующее имени контейнера хранилища, указанному в параметрах. Это приложение обходит правила безопасности при чтении и записи в контейнер.

firebase.initializeAdminApp({ storageBucket: "my-bucket" });

loadStorageRules({ storageBucket: string, rules: Object }) => Promise

Используйте это, чтобы задать правила для вашего хранилища.

Отправляет правила в локально управляемые контейнеры хранилища. Принимает объект параметров, который указывает ваш "storageBucket" и ваши "rules" как строки.

firebase
      .loadStorageRules({
        storageBucket: "my-bucket",
        rules: fs.readFileSync("/path/to/storage.rules", "utf8")
      });

apps() => [FirebaseApp]

Возвращает все инициализированные в данный момент тестовые и административные приложения.

Используйте это для очистки приложений между или после тестов (обратите внимание, что инициализированные приложения с активными прослушивателями предотвращают завершение работы JavaScript):

 Promise.all(firebase.apps().map(app => app.delete()))

assertFails(pr: Promise) => Promise

Возвращает обещание, которое отклоняется, если ввод успешен, и выполняется успешно, если ввод отклонен.

Используйте это, чтобы подтвердить, что чтение или запись в контейнер хранилища завершились неудачей:

firebase.assertFails(app.storage().ref("letters/private.doc").getMetadata());

assertSucceeds(pr: Promise) => Promise

Возвращает обещание, которое выполняется, если ввод успешен, и отклоняется, если ввод отклонен.

Используйте это, чтобы подтвердить, что чтение или запись в контейнер хранилища выполнены успешно:

firebase.assertFails(app.storage().ref("images/cat.png").getMetadata());