Skip to content

Gen2 / TypeScript: Snapshot from 'makeDocumentSnapshot' Doesn't Work for 'onDocumentCreated' #205

Open

Description

@JessRascal

Version info

firebase-functions-test: 3.1.0

firebase-functions: 4.4.1

firebase-admin: 11.9.0

Test case

Firebase Cloud Function

import { onDocumentCreated } from 'firebase-functions/v2/firestore';
import * as logger from 'firebase-functions/logger';
import { initializeApp } from 'firebase-admin/app';

initializeApp();

export const docCreateTest = onDocumentCreated(
  'messages/{documentId}',
  (event) => {
    const original = event.data?.data().original;

    logger.log('Uppercasing: ', original);

    const uppercase = original.toUpperCase();

    return event.data.ref.set({ uppercase }, { merge: true });
  }
);

Unit Test
Note: Using Vitest but I believe it would be the same result with Jest, etc.

import { describe, test, expect } from 'vitest';
import firebaseFunctionsTest from 'firebase-functions-test';
import * as admin from 'firebase-admin';

import { docCreateTest } from '../functions/index';

// NOTE: Don't need the service account file when using
// GOOGLE_APPLICATION_CREDENTIALS environment variable
const { wrap, firestore } = firebaseFunctionsTest({
  databaseURL: 'http://127.0.0.1:8080',
  projectId: 'project-id-test',
});

describe('Running tests...', () => {
  test('should return uppercase message', async () => {
    // create mock doc snapshot
    const snap = firestore.makeDocumentSnapshot(
      { original: 'hello' },
      'messages/abc'
    );

    // TODO: Workaround for https://github.com/firebase/firebase-functions-test/issues/163
    // @ts-ignore
    const wrapped = wrap(docCreateTest);
    await wrapped(snap);

    const result = await admin.firestore().doc('messages/abc').get();
    expect(result.data()?.uppercase).toBe('HELLO');
  });
});

Steps to reproduce

Run the unit test against the Firebase function and it will result in a Cannot read properties of undefined (reading 'original') when trying to create the 'original' variable in the function (const original = event.data?.data().original;).

This is because event.data.data() is undefined.

Expected behavior

The document snapshot created in the unit test with makeDocumentSnapshot is in the correct format to be passed into onDocumentCreated so that its data can be read.

Actual behavior

The 'original' variable cannot be created in the Firebase function because it can't read the data object in the passed in event resulting in the following error...

Cannot read properties of undefined (reading 'original')

I guess this is due to makeDocumentSnapshot creating a DocumentSnapshot and the event in onDocumentCreated expected to have a type of FirestoreEvent<QueryDocumentSnapshot | undefined, { documentId: string; }>.

I can't find any alternative to makeDocumentSnapshot that should be used in this scenario for gen2 functions though so I'm guessing this isn't supported yet?

If I create an object that contains the document snapshot as the value of a data parameter, it will work

test('should return uppercase message', async () => {
    // create mock doc snap
    const snap = firestore.makeDocumentSnapshot(
      { original: 'hello' },
      'messages/abc'
    );

    // TODO: My workaround for missing 'data' property when passing event
    const event = { data: snap }; // <-- CREATING THE NEW OBJECT

    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    const wrapped = wrap(docCreateTest);
    await wrapped(event); // <-- PASSING THE OBJECT I CREATED INSTEAD OF 'snap'

    const result = await admin.firestore().doc('messages/abc').get();
    expect(result.data()?.uppercase).toBe('HELLO');
  });

I'm not sure how much of this library is currently expected to work with the gen2 Firebase functions, but I couldn't find this issue already logged and the gen2 samples don't cover onDocumentCreated functions.

Cheers
Jess

Activity

  1. greg-md commented on Dec 14, 2023

    @greg-md

    The same issue happens with onDocumentWritten. The event.data.before.data() is retuning a strange object.

    const beforeSnap = fnTest.firestore.makeDocumentSnapshot(
      { foo: 'bar' },
      'Profile/1',
    );
    const afterSnap = fnTest.firestore.makeDocumentSnapshot(
      {},
      'Profile/1',
    );
    const change = fnTest.makeChange(beforeSnap, afterSnap);
    await wrapped(change);

    And in the v2 onDocumentWritten the event.data.before.data() returns:

    event { anObject: { a: 'bar' }, aNumber: 7 }
    

    I also catched that event.before.data() returns the valid data, but it throws a typescript error.

    The event object in console logs looks strange:

    event {
      specversion: '1.0',
      id: '9xaf968on9dz9x1ua9rr3',
      data: Change {
        before: QueryDocumentSnapshot {
          _fieldsProto: [Object],
          _ref: [DocumentReference],
          _serializer: [Serializer],
          _readTime: [Timestamp],
          _createTime: [Timestamp],
          _updateTime: [Timestamp]
        },
        after: QueryDocumentSnapshot {
          _fieldsProto: [Object],
          _ref: [DocumentReference],
          _serializer: [Serializer],
          _readTime: [Timestamp],
          _createTime: [Timestamp],
          _updateTime: [Timestamp]
        }
      },
      source: '',
      type: 'google.cloud.firestore.document.v1.written',
      time: '2023-12-14T12:43:48.031Z',
      location: 'us-central1',
      project: 'ci',
      database: '(default)',
      namespace: '(default)',
      document: 'Profile/undefined',
      params: {},
      before: QueryDocumentSnapshot {
        _fieldsProto: { foo: [Object] },
        _ref: DocumentReference {
          _firestore: [Firestore],
          _path: [QualifiedResourcePath],
          _converter: [Object]
        },
        _serializer: Serializer {
          createReference: [Function (anonymous)],
          createInteger: [Function (anonymous)],
          allowUndefined: false
        },
        _readTime: Timestamp { _seconds: 1702557828, _nanoseconds: 30000000 },
        _createTime: Timestamp { _seconds: 1702557828, _nanoseconds: 29000000 },
        _updateTime: Timestamp { _seconds: 1702557828, _nanoseconds: 30000000 }
      },
      after: DocumentSnapshot {
        _fieldsProto: undefined,
        _ref: DocumentReference {
          _firestore: [Firestore],
          _path: [QualifiedResourcePath],
          _converter: [Object]
        },
        _serializer: Serializer {
          createReference: [Function (anonymous)],
          createInteger: [Function (anonymous)],
          allowUndefined: false
        },
        _readTime: Timestamp { _seconds: 1702557828, _nanoseconds: 30000000 },
        _createTime: undefined,
        _updateTime: undefined
      }
    }
    
  2. greg-md commented on Dec 14, 2023

    @greg-md

    @JessRascal seems like v2 wrapped function expects a cloud event, not a change/snapshot object. So if you wrap your input into a cloud event object type, it works. See: https://github.com/firebase/firebase-functions-test/blob/master/src/cloudevent/mocks/firestore/helpers.ts#L68

    await wrapped({ data: change });
  3. wmurphyrd commented on Apr 5, 2024

    @wmurphyrd

    Would be nice if they updated the docs to reflect this

    https://firebase.google.com/docs/functions/unit-testing#constructing_test_data

  4. brandones commented on Oct 15, 2024

    @brandones

    The docs have still not been updated

    image

  5. moved this from Needs Acknowledgment to backlog in [Cloud] Extensions + Functionson Jun 5, 2025
  6. CorieW commented on May 28, 2026

    @CorieW
    Member

    Hey @JessRascal. I understand this to be a docs issue. I'll see about getting the docs updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions