firebase / firebase-functions-test

MIT License
232 stars 48 forks source link

Writes to document that triggered function are not persisted when running against emulator suite #85

Open itsravenous opened 3 years ago

itsravenous commented 3 years ago

Version info

firebase-functions-test: 0.2.3

firebase-functions: 3.13.0

firebase-admin: 9.4.1

Test case

myfunc.js: (based on this example about writing to the same document that triggered the function)

const functions = require("firebase-functions");

module.exports = functions.firestore
  .document("/users/{docId}")
  .onWrite(async (change, context) => {
    const newName = change.after.get("name");
    const oldName = change.before.get("name");
    if (oldName === newName) return null; // Prevent infinite loop

    await change.after.ref.set({
      name: newName,
      name_upper: newName.toUpperCase(),
    });
  });

myfunc.test.js:

const admin = require("firebase-admin");
admin.initializeApp();
const { project_id: projectId } = require("../../serviceAccount.json");

// Setup tests to be in "online" mode (see https://firebase.google.com/docs/functions/unit-testing#initializing)
const test = require("firebase-functions-test")(
  {
    databaseURL: `http://localhost:8080`,
    storageBucket: `${projectId}.appspot.com`,
    projectId,
  },
  __dirname + "/../../serviceAccount.json"
);

const myFunc = require("../myfunc");

it("writes to itself", async () => {
  // Make snapshot for state of database beforehand
  const beforeSnap = test.firestore.makeDocumentSnapshot({}, "users/foo");
  // Make snapshot for state of database after the change
  const afterSnap = test.firestore.makeDocumentSnapshot(
    { name: "foo" },
    "users/foo"
  );
  const change = test.makeChange(beforeSnap, afterSnap);
  // Call wrapped function with the Change object
  const wrapped = test.wrap(myFunc);
  await wrapped(change);

  // Assert that the user document was updated
  expect(afterSnap.get("name_upper")).toEqual("FOO");
});

Steps to reproduce

  1. Start emulator
  2. Run test, prefixed with FIRESTORE_EMULATOR_HOST=localhost:8080

Expected behavior

afterSnap should have a new field, name_upper with value FOO

Actual behavior

It only has the original field , name:

expect(received).toEqual(expected) // deep equality

    Expected: "FOO"
    Received: undefined

I am, however, able to write to a new document in the emulator Firestore instance from within the cloud function, e.g:

const ref = admin.firestore().doc('/users/bar');
await ref.set({name: 'bar'}); // Document is now visible in emulator Firestore instance
ellioseven commented 3 years ago

@itsravenous Did you ever manage to figure this out? Running into the same problem.

itsravenous commented 3 years ago

@itsravenous Did you ever manage to figure this out? Running into the same problem.

Nope, I ended up writing integration tests instead, using firebase-admin to write data to firestore and then reading it back within a wait function to make assertions on it to verify the cloud function did its job. Not ideal, but I had to get some sort of coverage and move on!