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

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

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

Используйте модуль @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>

Это служебная функция тестового примера.

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

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

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

Это служебная функция тестового примера.

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

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

Cloud Storage

Cloud Storage

RulesTestEnvironment.clearStorage() => Promise<void>

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

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

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

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

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

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

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» и ваши «правила» в виде строк.

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

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

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

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» и ваши «правила» в виде строк.

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