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

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

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

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

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

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

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

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

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

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

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 распространяет библиотеку модульного тестирования правил безопасности как с версией 9 JavaScript SDK, так и с версией 8 SDK. API библиотек существенно различаются. Мы рекомендуем библиотеку тестирования версии 9, которая более оптимизирована и требует меньше настроек для подключения к эмуляторам, что позволяет безопасно избежать случайного использования производственных ресурсов. Для обеспечения обратной совместимости мы продолжаем предоставлять библиотеку тестирования версии 8 .

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

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

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

Импорт библиотеки осуществляется с помощью модульных операторов импорта версии 9. Например:

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().doc('/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()

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

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

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 (модульной версии 9 или совместимой с версией 9).

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-адрес экземпляра базы данных реального времени. Если указан, возвращает экземпляр для эмулируемой версии пространства имен с параметрами, извлеченными из URL-адреса.

Cloud Storage

Cloud Storage

RulesTestEnvironment.clearStorage() => Promise<void>

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

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

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

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

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

Cloud Firestore

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

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

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") и правила ("rules".

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 версии 8

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

Cloud Firestore

Cloud Firestore

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

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

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") и правила ("rules".

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