При разработке приложения вам может потребоваться ограничить доступ к базе данных Cloud Firestore . Однако перед запуском вам понадобятся более детальные Cloud Firestore Security Rules . С помощью эмулятора Cloud Firestore , помимо создания прототипа и тестирования основных функций и поведения приложения, вы можете писать модульные тесты, проверяющие поведение Cloud Firestore Security Rules .
Быстрый старт
Для нескольких базовых тестовых случаев с простыми правилами попробуйте пример быстрого старта .
Понимание Cloud Firestore Security Rules
Реализуйте Cloud Firestore Security Rules Firebase Authentication и безопасности Cloud Firestore для бессерверной аутентификации, авторизации и проверки данных при использовании библиотек мобильных и веб-клиентов.
Cloud Firestore Security Rules состоят из двух частей:
- Оператор
match
, который идентифицирует документы в вашей базе данных. - Выражение
allow
, которое управляет доступом к этим документам.
Firebase Authentication проверяет учетные данные пользователей и обеспечивает основу для систем доступа на основе пользователей и ролей.
Каждый запрос к базе данных из библиотеки мобильного/веб-клиента Cloud Firestore проверяется на соответствие вашим правилам безопасности перед чтением или записью данных. Если правила запрещают доступ к любому из указанных путей к документам, весь запрос завершается ошибкой.
Дополнительную информацию о Cloud Firestore Security Rules можно найти в статье Начало работы с Cloud Firestore Security Rules .
Установить эмулятор
Чтобы установить эмулятор Cloud Firestore , используйте Firebase CLI и выполните следующую команду:
firebase setup:emulators:firestore
Запустить эмулятор
Начните с инициализации проекта Firebase в рабочем каталоге. Это стандартный первый шаг при использовании Firebase CLI .
firebase init
Запустите эмулятор с помощью следующей команды. Эмулятор будет работать, пока вы не завершите процесс:
firebase emulators:start --only firestore
Во многих случаях требуется запустить эмулятор, выполнить набор тестов и затем завершить работу эмулятора после их выполнения. Это можно легко сделать с помощью команды emulators:exec
:
firebase emulators:exec --only firestore "./my-test-script.sh"
При запуске эмулятор попытается работать на порту по умолчанию (8080). Вы можете изменить порт эмулятора, изменив раздел "emulators"
файла firebase.json
:
{ // ... "emulators": { "firestore": { "port": "YOUR_PORT" } } }
Перед запуском эмулятора
Прежде чем начать использовать эмулятор, имейте в виду следующее:
- Эмулятор изначально загрузит правила, указанные в поле
firestore.rules
вашего файлаfirebase.json
. Он ожидает имя локального файла, содержащего Cloud Firestore Security Rules , и применяет эти правила ко всем проектам. Если вы не укажете путь к локальному файлу или не используете методloadFirestoreRules
, как описано ниже, эмулятор будет считать все проекты имеющими открытые правила. - Хотя большинство Firebase SDK работают с эмуляторами напрямую, только библиотека
@firebase/rules-unit-testing
поддерживаетauth
в правилах безопасности, что значительно упрощает модульное тестирование. Кроме того, библиотека поддерживает несколько функций, специфичных для эмуляторов, например, очистку всех данных, перечисленных ниже. - Эмуляторы также будут принимать производственные токены Firebase Auth, предоставляемые через клиентские SDK, и соответствующим образом оценивать правила, что позволяет подключать ваше приложение напрямую к эмуляторам для интеграционных и ручных тестов.
Запуск локальных модульных тестов
Запускайте локальные модульные тесты с помощью JavaScript SDK v9
Firebase распространяет библиотеку модульного тестирования Security Rules как с JavaScript SDK версии 9, так и с SDK версии 8. API библиотек существенно различаются. Мы рекомендуем библиотеку тестирования версии 9, которая более оптимизирована и требует меньше настроек для подключения к эмуляторам, что позволяет избежать случайного использования ресурсов производства. Для обеспечения обратной совместимости мы по-прежнему предоставляем библиотеку тестирования версии 8 .
- Распространенные методы тестирования и служебные функции в SDK v9
- Методы тестирования, специфичные для эмулятора, в SDK v9
Используйте модуль @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
. - Настройка тестовых данных без запуска правил с использованием удобного метода, позволяющего временно их обходить,
RulesTestEnvironment.withSecurityRulesDisabled
. - Настройка тестового набора и перехватов «до/после» для каждого теста с вызовами для очистки тестовых данных и среды, такими как
RulesTestEnvironment.cleanup()
илиRulesTestEnvironment.clearFirestore()
. - Реализация тестовых случаев, имитирующих состояния аутентификации, с использованием
RulesTestEnvironment.authenticatedContext
иRulesTestEnvironment.unauthenticatedContext
.
Общие методы и функции полезности
См. также методы тестирования, специфичные для эмулятора, в SDK v9 .
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
, который ведёт себя как аутентифицированный пользователь аутентификации. К запросам, созданным через возвращаемый контекст, будет прикреплён фиктивный токен аутентификации. При необходимости можно передать объект, определяющий пользовательские утверждения или переопределения для полезных данных токена аутентификации.
Используйте возвращенный объект контекста теста в своих тестах для доступа к любым настроенным экземплярам эмулятора, включая настроенные с помощью 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
, который ведёт себя как клиент, не прошедший аутентификацию. К запросам, созданным через возвращаемый контекст, не будут прикреплены токены аутентификации 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'), { ... });
Методы, специфичные для эмулятора
См. также общие методы тестирования и служебные функции в v9 SDK .
RulesTestEnvironment.clearFirestore() => Promise<void>
Этот метод очищает данные в базе данных Firestore, принадлежащие projectId
настроенному для эмулятора Firestore.
RulesTestContext.firestore(settings?: Firestore.FirestoreSettings) => Firestore;
Этот метод получает экземпляр Firestore для данного тестового контекста. Возвращаемый экземпляр Firebase JS Client SDK можно использовать с API клиентского SDK (модульным или совместимым с v9).
Визуализация оценок правил
Эмулятор Cloud Firestore позволяет визуализировать клиентские запросы в пользовательском интерфейсе Emulator Suite, включая трассировку оценки для правил безопасности Firebase.
Откройте вкладку Firestore > Запросы , чтобы просмотреть подробную последовательность оценки для каждого запроса.
Создание отчетов об испытаниях
После запуска набора тестов вы можете получить доступ к отчетам о покрытии тестами, в которых показано, как оценивалось каждое из ваших правил безопасности.
Чтобы получить отчёты, отправьте запрос к открытой конечной точке эмулятора во время его работы. Для браузерной версии используйте следующий URL:
http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage.html
Это разобьёт ваши правила на выражения и подвыражения, на которые можно навести курсор, чтобы получить дополнительную информацию, включая количество вычислений и возвращаемых значений. Чтобы получить необработанную версию этих данных в формате JSON, включите в запрос следующий URL:
http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage
Различия между эмулятором и продакшеном
- Вам не нужно явно создавать проект Cloud Firestore . Эмулятор автоматически создаёт любой экземпляр, к которому осуществляется доступ.
- Эмулятор Cloud Firestore не работает с обычной процедурой Firebase Authentication . Вместо этого в Firebase Test SDK мы предоставили метод
initializeTestApp()
в библиотекеrules-unit-testing
, который принимает полеauth
. Дескриптор Firebase, созданный с помощью этого метода, будет вести себя так, как будто он успешно прошёл аутентификацию в качестве любого указанного вами объекта. Если передатьnull
, он будет вести себя как неаутентифицированный пользователь (например, правилаauth != null
приведут к ошибке).
Устранение известных проблем
При использовании эмулятора Cloud Firestore вы можете столкнуться со следующими известными проблемами. Следуйте приведенным ниже инструкциям для устранения любых нестандартных проблем. Эти заметки написаны с учётом библиотеки модульного тестирования Security Rules, но общие подходы применимы к любому Firebase SDK.
Поведение теста непоследовательно
Если ваши тесты периодически проходят и не проходят, даже без внесения каких-либо изменений в сами тесты, вам может потребоваться проверить их последовательность. Большинство взаимодействий с эмулятором происходит асинхронно, поэтому дважды проверьте последовательность всего асинхронного кода. Вы можете исправить последовательность, либо объединив обещания в цепочку, либо свободно используя нотацию await
.
В частности, рассмотрите следующие асинхронные операции:
- Установка правил безопасности, например, с помощью
initializeTestEnvironment
. - Чтение и запись данных, например, с помощью
db.collection("users").doc("alice").get()
. - Операционные утверждения, включая
assertSucceeds
иassertFails
.
Тесты проходят только при первой загрузке эмулятора.
Эмулятор сохраняет состояние. Он хранит все записанные в него данные в памяти, поэтому при завершении работы эмулятора все данные теряются. Если вы запускаете несколько тестов с одним и тем же идентификатором проекта, каждый тест может генерировать данные, которые могут повлиять на последующие тесты. Вы можете обойти это поведение любым из следующих способов:
- Используйте уникальные идентификаторы проектов для каждого теста. Обратите внимание: в этом случае вам потребуется вызывать
initializeTestEnvironment
в рамках каждого теста; правила автоматически загружаются только для идентификатора проекта по умолчанию. - Измените структуру своих тестов так, чтобы они не взаимодействовали с ранее записанными данными (например, используйте отдельную коллекцию для каждого теста).
- Удалите все данные, записанные во время теста.
Тестовая установка очень сложная
При настройке теста вам может потребоваться изменить данные способом, который не допускается Cloud Firestore Security Rules . Если ваши правила усложняют настройку теста, попробуйте использовать RulesTestEnvironment.withSecurityRulesDisabled
в настройках, чтобы чтение и запись не вызывали ошибок PERMISSION_DENIED
.
После этого ваш тест сможет выполнять операции от имени аутентифицированного или неаутентифицированного пользователя, используя RulesTestEnvironment.authenticatedContext
и unauthenticatedContext
соответственно. Это позволяет проверить, что ваши Cloud Firestore Security Rules корректно разрешают/запрещают различные ситуации.