[英]How to mock data for testing Firestore with emulator
I am testing my Firestore security rules and have the following setup:我正在测试我的 Firestore 安全规则并进行以下设置:
// firestore.spec.js
/**
* Creates a new client FirebaseApp with authentication and returns the Firestore instance.
* Also optionally seeds the project with mock data
*/
setupFirestoreDb = async (auth, data) => {
// initialize test app
const PROJECT_ID = "my-test-project-xyz";
const app = firebase.initializeTestApp({ projectId: PROJECT_ID, auth });
const db = app.firestore();
// Write mock documents
if (data) {
for (const key in data) {
const ref = db.doc(key);
ref.set(data[key]);
}
}
return db;
};
beforeEach(async () => {
// Clear the database between tests
await firebase.clearFirestoreData({ projectId: PROJECT_ID });
});
before(async () => {
// Load the rules file before the tests begin
const rules = fs.readFileSync("firestore.rules", "utf8");
await firebase.loadFirestoreRules({ projectId: PROJECT_ID, rules });
});
after(async () => {
// Delete all the FirebaseApp instances created during testing
// Note: this does not affect or clear any data
await Promise.all(firebase.apps().map((app) => app.delete()));
});
const mockData = {
'users/alice': {
foo: 'bar',
nestedData: {
baz: 'fae'
}
},
'users/bob': {
foo: 'bar',
nestedData: {
baz: 'fae'
}
},
// ... more data
}
// run the test suite
describe("Test Security Rules", () => {
it("should let any signed-in user to read any public profile", async () => {
let db = await setupFirestoreDb({uid: "bob"}, mockData);
aliceRef = db.collection("users").doc("alice");
await firebase.assertSucceeds(aliceRef.get()); // fails
});
// more test cases
...
});
And my security rules file:我的安全规则文件:
// firestore.rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// define common functions used across collections/documents
function userExists() {
return exists(/databases/$(database)/documents/users/$(request.auth.uid));
}
// Fetch a user from Firestore by UID
function getUserData(uid) {
return get(/databases/$(database)/documents/users/$(uid)).data
}
function isValidUser() {
let userData = request.resource.data;
return userData.name != null && userData.phoneNumber != null;
}
// ... more functions
// lock the entire database by default
match /{document=**} {
allow read, write: if false;
}
// rules for users collection
match /users/{userId} {
allow read: if isSignedIn() && getUserData(userId) != null; // && userExists(userId); // make sure user being read exists
allow write: if isSignedIn() && isUser(userId) && isValidUser(); // only an authenticated user can create own account
// ... other rules for nested data (subcollections)
}
// ... more rules
}
}
FirebaseError: false for 'get' @ L*, Null value error. for 'get' @ L*
FirebaseError: false for 'get' @ L*, Null value error. for 'get' @ L*
, which occurs because I believe the function getUserData()
returns null (same for userExists()
). FirebaseError: false for 'get' @ L*, Null value error. for 'get' @ L*
,这是因为我相信 function getUserData()
返回 null (与userExists()
相同)。
getUserData()
to return true because the document for user 'alice' is created based on the mock data.相反,我希望getUserData()
返回 true,因为用户“alice”的文档是基于模拟数据创建的。 Is it a problem with the Firestore emulator or is there something wrong with how I set up my mock data & test fixture?是 Firestore 模拟器有问题还是我设置模拟数据和测试夹具的方式有问题? I used the Firebase simulator on the console to test the same rules against a real database and the rules work ( getUserData()
and userExists()
work as expected)我在控制台上使用 Firebase 模拟器针对真实数据库测试相同的规则并且规则有效( getUserData()
和userExists()
按预期工作)
The code is largely based on this fireship tutorial and the official firebase unit testing tutorial代码主要基于这个fireship 教程和官方firebase 单元测试教程
Turns out I had missed to see a crucial error in the logs letting me know that my user objects were actually not being written:结果我错过了在日志中看到一个关键错误,让我知道我的用户对象实际上没有被写入:
(node:25284) UnhandledPromiseRejectionWarning: FirebaseError: 7 PERMISSION_DENIED:
false for 'create' @ L*, Property phoneNumber is undefined on object. for 'create' @ L*
The fix was embarrassingly simple: add the name, phone number fields in the test fixture data to match the fields are enforced by the model/rules.修复非常简单:在测试夹具数据中添加姓名、电话号码字段以匹配模型/规则强制执行的字段。 That way, the get()
and exists()
functions would actually fetch against existing user data.这样, get()
和exists()
函数实际上会获取现有用户数据。
TL;DR - pay attention to the error and warning logs reported by the emulator when enforcing rules TL;DR - 在执行规则时注意模拟器报告的错误和警告日志
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.