我正在尝试测试我编写的firestore安全规则,但每次我试图运行我的测试时,我都得到超时错误。
在运行测试之前,我使用以下命令启动firestore模拟器:
❯ npm run firebase:run:emulator
> warzywniax@0.0.0 firebase:run:emulator
> cd firebase && firebase emulators:start --project=warzywniax --import=./seed --export-on-exit
i emulators: Starting emulators: auth, firestore, storage
i firestore: Importing data from /firebase/seed/firestore_export/firestore_export.overall_export_metadata
i firestore: Firestore Emulator logging to firestore-debug.log
✔ firestore: Firestore Emulator UI websocket is running on 9150.
i auth: Importing config from /firebase/seed/auth_export/config.json
i auth: Importing accounts from /firebase/seed/auth_export/accounts.json
i ui: Emulator UI logging to ui-debug.log
┌─────────────────────────────────────────────────────────────┐
│ ✔ All emulators ready! It is now safe to connect your app. │
│ i View Emulator UI at http://127.0.0.1:4000/ │
└─────────────────────────────────────────────────────────────┘
┌────────────────┬────────────────┬─────────────────────────────────┐
│ Emulator │ Host:Port │ View in Emulator UI │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Authentication │ 127.0.0.1:9099 │ http://127.0.0.1:4000/auth │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Firestore │ 127.0.0.1:8080 │ http://127.0.0.1:4000/firestore │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Storage │ 127.0.0.1:9199 │ http://127.0.0.1:4000/storage │
└────────────────┴────────────────┴─────────────────────────────────┘
Emulator Hub running at 127.0.0.1:4400
Other reserved ports: 4500, 9150
看起来一切正常。我的firestore模拟器托管在127.0.0.1:8080下并且通过我的web浏览器访问这个模拟器没有任何问题。
在我运行任何测试之前,我在setupFilesAfterEnv
jest hook上运行一些设置脚本。整个设置脚本如下所示:
import { initializeTestEnvironment, RulesTestEnvironment } from '@firebase/rules-unit-testing';
import firebase from 'firebase/compat/';
export let testEnvironment: RulesTestEnvironment;
export let guestUserContext: firebase.firestore.Firestore;
beforeAll(async () => {
testEnvironment = await initializeTestEnvironment({
firestore: {
host: '127.0.0.1',
port: 8080,
},
projectId: 'warzywniax',
});
guestUserContext = testEnvironment.unauthenticatedContext().firestore();
console.info('TEST_ENV is ready:n', testEnvironment);
console.info('GUEST_USER is ready:n', guestUserContext);
});
然后尝试运行一些简单的测试,像这样:
import { assertSucceeds } from '@firebase/rules-unit-testing';
import { guestUserContext } from '../integration-setup';
describe('Offers collection tests', () => {
it('should allow to read offers for any user', async () => {
const testQuery = guestUserContext.collection('offers');
expect(await assertSucceeds(testQuery.get()));
});
});
返回以下错误:
FAIL test/integration/firebase/firestore.rules.spec.ts (5.234 s)
Offers collection tests
✕ should allow to read offers for any user (5001 ms)
● Offers collection tests › should allow to read offers for any user
thrown: "Exceeded timeout of 5000 ms for a test.
Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout.
我已经检查了收集是否有任何问题,但如果我记录testQuery
,它看起来很好。
我也按照上面错误的建议,将超时时间延长到30s (--testTimeout=30000
)。什么产生了奇怪的结果。我的测试通过了,但还是出现了这样的错误:
console.error
[2023-03-30T15:45:44.552Z] @firebase/firestore: Firestore (9.17.1): Could not reach Cloud Firestore backend. Backend didn't respond within 10 seconds.
This typically indicates that your device does not have a healthy Internet connection at the moment. The client will operate in offline mode until it is able to successfully connect to the backend.
at Logger.defaultLogHandler [as _logHandler] (node_modules/@firebase/logger/src/logger.ts:115:57)
at Logger.Object.<anonymous>.Logger.error (node_modules/@firebase/logger/src/logger.ts:210:5)
at x (node_modules/@firebase/firestore/dist/index.esm2017.js:124:11)
at Pu.au (node_modules/@firebase/firestore/dist/index.esm2017.js:13778:20)
at nc.op (node_modules/@firebase/firestore/dist/index.esm2017.js:13748:14)
at node_modules/@firebase/firestore/dist/index.esm2017.js:14307:14
at node_modules/@firebase/firestore/dist/index.esm2017.js:17414:73
at node_modules/@firebase/firestore/dist/index.esm2017.js:17447:54
PASS test/integration/firebase/firestore.rules.spec.ts (10.335 s)
Offers collection tests
✓ should allow to read offers for any user (10168 ms)
在我看来,什么应该使这个测试失败。你有什么想法可能是错误的,我该如何解决这个问题?
我遇到了这个问题。我有几个与Firebase规则相关的Jest测试,所有这些测试都由于超时而失败,而我所有的Firebase数据库规则测试都通过了。
经过大量的研究,我了解到的是,至少在我这边,这个问题与使用jsdom
的测试环境有关。当我切换到Jest的默认测试环境,即node
时,测试又开始通过了。
我找到的主要帮助是这个线程(原文如此)React native repo。https://github.com/facebook/react-native/issues/36342
整篇文章中最重要的一段是:这里的根本问题是RN的Jest环境现在只将react-native作为导出条件(而不是节点)导出,这意味着导出节点目标的包现在可能会以不同的方式解析。在firebase的情况下,它默认返回到ESM。老实说,这应该被标记为对Jest设置的突破性更改-抱歉。">
我希望这能帮助到别人。
我运行了您的设置,并且能够通过测试。检查这里的版本号,看看它是否是任何问题的原因。输出也可以以有意义的方式不同:
import { initializeTestEnvironment } from '@firebase/rules-unit-testing';
export let testEnvironment;
export let guestUserContext;
beforeEach(async () => {
testEnvironment = await initializeTestEnvironment({
firestore: {
host: '127.0.0.1',
port: 8080,
},
projectId: 'warzywniax',
});
guestUserContext = testEnvironment.unauthenticatedContext().firestore();
console.info('TEST_ENV is ready:n', testEnvironment);
console.info('GUEST_USER is ready:n', guestUserContext);
});
import { initializeTestEnvironment, assertSucceeds } from '@firebase/rules-unit-testing';
import { guestUserContext } from './lib.js';
describe('Offers collection tests', () => {
it('should allow to read offers for any user', async () => {
const testQuery = guestUserContext.collection('offers');
await assertSucceeds(testQuery.get());
});
});
node . js package.json:
{
"name": "rulestestingso",
"version": "1.0.0",
"description": "Testing this",
"main": "main.js",
"type": "module",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"author": "chris",
"license": "ISC",
"dependencies": {
"@firebase/rules-unit-testing": "^2.0.7",
"firebase": "^9.19.0",
"jest": "^29.5.0"
}
}
$ mocha main.js
Offers collection tests
TEST_ENV is ready:
RulesTestEnvironmentImpl {
projectId: 'warzywniax',
emulators: { firestore: { host: '127.0.0.1', port: 8080 } },
contexts: Set(1) {
RulesTestContextImpl {
projectId: 'warzywniax',
emulators: [Object],
authToken: undefined,
destroyed: false,
envDestroyed: false,
app: [FirebaseAppImpl]
}
},
destroyed: false
}
GUEST_USER is ready:
Firestore {
_delegate: Firestore {
_authCredentials: FirebaseAuthCredentialsProvider {
authProvider: [Provider],
currentUser: [User],
tokenCounter: 0,
forceRefresh: false,
auth: null
},
_appCheckCredentials: FirebaseAppCheckTokenProvider {
appCheckProvider: [Provider],
forceRefresh: false,
appCheck: null,
latestAppCheckToken: null
},
_databaseId: DatabaseId { projectId: 'warzywniax', database: '(default)' },
_app: FirebaseAppImpl {
_isDeleted: false,
_options: [Object],
_config: [Object],
_name: '_Firebase_RulesUnitTesting_1680224130695_0.0042143144476949335',
_automaticDataCollectionEnabled: false,
_container: [ComponentContainer]
},
type: 'firestore',
_persistenceKey: '_Firebase_RulesUnitTesting_1680224130695_0.0042143144476949335',
_settings: FirestoreSettingsImpl {
host: '127.0.0.1:8080',
ssl: false,
credentials: undefined,
ignoreUndefinedProperties: false,
cache: undefined,
cacheSizeBytes: 41943040,
experimentalForceLongPolling: false,
experimentalAutoDetectLongPolling: false,
useFetchStreams: true
},
_settingsFrozen: false,
_queue: AsyncQueueImpl {
tail: [Promise],
retryableOps: [],
_isShuttingDown: false,
delayedOperations: [],
failure: null,
operationInProgress: false,
skipNonRestrictedTasks: false,
timerIdsToSkip: [],
backoff: [ExponentialBackoff],
visibilityHandler: [Function (anonymous)]
}
},
_persistenceProvider: IndexedDbPersistenceProvider {},
INTERNAL: { delete: [Function: delete] },
_appCompat: FirebaseAppImpl {
_delegate: FirebaseAppImpl {
_isDeleted: false,
_options: [Object],
_config: [Object],
_name: '_Firebase_RulesUnitTesting_1680224130695_0.0042143144476949335',
_automaticDataCollectionEnabled: false,
_container: [ComponentContainer]
},
firebase: <ref *1> {
__esModule: true,
initializeApp: [Function: initializeAppCompat],
app: [Function],
registerVersion: [Function: registerVersion],
setLogLevel: [Function: setLogLevel],
onLog: [Function: onLog],
apps: [Getter],
SDK_VERSION: '9.19.0',
INTERNAL: [Object],
default: [Circular *1],
database: [Function],
firestore: [Function],
storage: [Function]
},
container: ComponentContainer {
name: '_Firebase_RulesUnitTesting_1680224130695_0.0042143144476949335',
providers: [Map]
}
}
}
✔ should allow to read offers for any user (1049ms)
1 passing (1s)