简体   繁体   English

如何模拟数据以使用模拟器测试 Firestore

[英]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 
  }
}

The test fails with:测试失败:

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()相同)。

Instead, I expected 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM