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

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

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

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

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

Библиотека импортируется с помощью операторов модульного импорта версии 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(), '/users/alice'), { ... });

RulesTestEnvironment.unauthenticatedContext() => RulesTestContext

Этот метод создаёт RulesTestContext , который ведёт себя как клиент, не прошедший Authentication . К запросам, созданным через возвращаемый контекст, не будут прикреплены токены аутентификации Firebase.

Используйте возвращенный объект контекста теста в своих тестах для доступа к любым настроенным экземплярам эмулятора, включая настроенные с помощью 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 (модульным или совместимым с 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;

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

Запускайте локальные модульные тесты с помощью 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 v8

Выберите продукт, чтобы увидеть методы, используемые 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());