При создании приложения вы можете ограничить доступ к базе данных Cloud Firestore. Однако перед запуском вам потребуется более детальная Cloud Firestore Security Rules. Эмулятор Cloud Firestore позволяет не только создавать прототипы и тестировать общие функции и поведение приложения, но и писать модульные тесты для проверки поведения Cloud Firestore Security Rules.
Краткое руководство
Чтобы ознакомиться с несколькими базовыми тестовыми примерами с простыми правилами, попробуйте пример быстрого запуска.
Понять Cloud Firestore Security Rules
Реализуйте Firebase Authentication и Cloud Firestore Security Rules для бессерверной аутентификации, авторизации и проверки данных при использовании клиентских библиотек для мобильных устройств и веб-клиентов.
Cloud Firestore Security Rules состоят из двух частей:
- Инструкция
match, которая идентифицирует документы в базе данных. - Выражение
allow, которое управляет доступом к этим документам.
Firebase Authentication проверяет учетные данные пользователей и обеспечивает основу для систем доступа на основе пользователей и ролей.
Каждый запрос к базе данных из клиентской библиотеки правил для мобильных устройств или веб-клиентов оценивается на соответствие правилам безопасности перед чтением или записью любых данных. Если правила запрещают доступ к любому из указанных путей к документам, весь запрос завершается ошибкой.
Подробнее Cloud Firestore Security Rules описан в статье Начало работы с Cloud Firestore Security Rules.
Установите эмулятор
Чтобы установить эмулятор Cloud Firestore, используйте интерфейс командной строки Firebase и выполните команду ниже.
firebase setup:emulators:firestore
Запустите эмулятор
Сначала инициализируйте проект Firebase в рабочем каталоге. Это распространенный первый шаг при использовании интерфейса командной строки Firebase.
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 версии 9
Firebase распространяет библиотеку модульного тестирования правил безопасности вместе с JavaScript SDK версий 8 и 9. API библиотек значительно различаются. Мы рекомендуем использовать библиотеку тестирования версии 9, которая более оптимизирована и требует меньше настроек для подключения к эмуляторам, что позволяет безопасно избежать случайного использования рабочих ресурсов. Для обратной совместимости мы по-прежнему предоставляем библиотеку тестирования v8.
- Распространенные методы тестирования и служебные функции в SDK версии 9
- Методы тестирования для эмулятора в SDK версии 9
Для взаимодействия с эмулятором, который работает локально, используйте модуль @firebase/rules-unit-testing. Если вы получаете ошибки ECONNREFUSED или ошибки, связанные с тайм-аутом, проверьте, запущен ли эмулятор.
Мы настоятельно рекомендуем использовать последнюю версию Node.js, чтобы вы могли использовать нотацию async/await. Почти все действия, которые вы захотите протестировать, включают асинхронные функции, а модуль тестирования предназначен для работы с кодом на основе Promise.
Библиотека модульного тестирования правил v9 всегда знает об эмуляторах и никогда не затрагивает ваши рабочие ресурсы.
Вы импортируете библиотеку, используя операторы модульного импорта v9. Пример:
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 версии 9.
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().doc('/users/alice'), { ... });
RulesTestEnvironment.unauthenticatedContext() => RulesTestContext
Этот метод создает RulesTestContext, который ведет себя как клиент, не вошедший в аккаунт с помощью аутентификации. К запросам, созданным с помощью возвращенного контекста, не будут прикреплены токены 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()
Запустите функцию настройки теста с контекстом, который ведет себя так, как если бы правила безопасности были отключены.
Этот метод принимает функцию обратного вызова, которая принимает контекст обхода правил безопасности и возвращает объект Promise. Контекст будет удален после того, как объект Promise будет распознан или отклонен.
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'), { ... });Методы, относящиеся к эмулятору
Также ознакомьтесь с распространенными методами тестирования и служебными функциями в SDK версии 9.
RulesTestEnvironment.clearFirestore() => Promise<void>
Этот метод очищает данные в базе данных Firestore, которая принадлежит projectId, настроенному для эмулятора Firestore.
RulesTestContext.firestore(settings?: Firestore.FirestoreSettings) => Firestore;
Этот метод получает экземпляр Firestore для этого контекста тестирования. Возвращенный экземпляр Firebase JS Client SDK можно использовать с API клиентского SDK (модульным v9 или совместимым v9).
Визуализация оценок правил
Эмулятор Cloud Firestore позволяет визуализировать запросы клиентов в интерфейсе Emulator Suite, включая трассировку оценки для правил безопасности Firebase.
Откройте вкладку Firestore > Requests (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 могут возникать описанные ниже проблемы. Ниже приведены инструкции по устранению неполадок. Эти примечания написаны с учетом библиотеки модульного тестирования правил безопасности, но общие подходы применимы к любому 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 разрешает или запрещает разные случаи.