firebase / firebase-functions-test

MIT License
232 stars 48 forks source link

firebase-admin 6.x.x compatibility #37

Closed kylehotchkiss closed 4 years ago

kylehotchkiss commented 5 years ago

Version info

firebase-functions-test: 0.1.6

firebase-functions: 2.1.0

firebase-admin: 6.4.0

Test case

With the above package versions, run this:

      const test = require('firebase-functions-test')();

      const snapshot = test.firestore.makeDocumentSnapshot({
        fields: {
          email_address: 'test@test.com'
        }
      }, 'signups/testing-id');

Steps to reproduce

Run above

Expected behavior

Firestore document snapshot created

Actual behavior

    TypeError: firestoreService.snapshot_ is not a function

      16 | 
    > 17 |       const snapshot = test.firestore.makeDocumentSnapshot({
      18 |         fields: {
      19 |           email_address: 'test@test.com'
      20 |         }

      at Object.makeDocumentSnapshot (node_modules/firebase-functions-test/lib/providers/firestore.js:55:29)
      at Object.<anonymous> (test/signups-pipeline.spec.js:17:39)

Comments

Another user's Stack Overflow report of same bug: https://stackoverflow.com/questions/53723486/typeerror-firestoreservice-snapshot-is-not-a-function

My guess is maybe firebase-admin version 6.x.x is causing it?

MohsenElgendy commented 5 years ago

@kylehotchkiss any luck with that?

abeisgoat commented 5 years ago

Hmm, thanks for reporting this. I'll be doing some work on this lib in the coming weeks and I'll be investigating this in that work.

stanly-sovereignnw commented 5 years ago

I tried downgrading from v7.2.0 to v5.13.0 but the issue still persists. Does anyone have a solution?

laurenzlong commented 4 years ago

Hi everyone, I'm unable to reproduce the issue, I added unit test to verify that makeDocumentSnapshot works: https://github.com/firebase/firebase-functions-test/pull/53

I've verified it works with both v6 and v8 of firebase-admin.

wujekbogdan commented 4 years ago

@kylehotchkiss

I was able to reproduce it:

Zrzut ekranu 2020-01-17 o 00 00 40

node 8 / node 10

{
    "firebase-admin": "^8.9.1",
    "firebase-functions": "3.3.0",
    "firebase-tools": "^7.12.1",
    "@firebase/testing": "^0.16.6",
}
Zloka commented 4 years ago

Happens to me as well, my dependencies:

"dependencies": {
    "firebase-admin": "^9.0.0",
    "firebase-functions": "^3.8.0",
  },
  "devDependencies": {
    "firebase-functions-test": "^0.2.1",
    "@firebase/testing": "^0.20.9",
    "jest": "^26.1.0"
  },
blaur commented 3 years ago

@Zloka I'm seeing the exact same problem with your dependencies. DId you find a solution?

blaur commented 3 years ago

@laurenzlong Maybe it would make sense to re-open this? I'm seeing this with the updates dependencies as per comment from @Zloka. Have no idea how to get out of this and it is blocking our testing at the moment.

Any suggestions on how to fix it?

Zloka commented 3 years ago

@blaur Sadly, I don't think we were able to ever solve this one. Rather than unit testing the background functions, we decided to "implicitly" test them by setting up a Firestore and Functions emulator, writing data to Firestore that should cause the background function to trigger, and then assert that the background function ran correctly by observing the expected change to the Firestore data after some time using the wait-for-expect package.

To be honest, in the end I would probably opt for both unit testing and our "integration" test if possible, so if you are able to solve the problem I would happily hear it, and ask away if you think our approach sounds like a feasible alternative :)

blaur commented 3 years ago

@Zloka Really appreciate your answer.

I've also now going in that direction. I've for a while had a successful unit test suite running using the @firebase/testing library to initialize an admin app (initializeAdminApp) and run all firestore crud and services in those. I have the emulator running (and without it of course nothing runs) but it does not seem to persist anything in firestore so therefore no functions are triggered based on it (I'm looking at the emulator UI locally).

So emulators are running fine and without the emulators then I get Error: 14 UNAVAILABLE: No connection established... when calling firestore.collection or similar, so I know that it is accessing it. But when looking at the UI there is nothing. image

Something that I am missing here that you might be able to answer?

Zloka commented 3 years ago

@blaur Sadly I have yet to start using the Emulator UI myself, so I can't speak for any quirks it might have, but I can try to outline the structure of my tests and perhaps that might help you find the culprit.

Essentially, each test just uses these imports:

import * as firebase from '@firebase/testing'; // ^0.20.9
import waitForExpect from 'wait-for-expect';   // ^3.0.2

Some initial setup before any testing starts (I do this separately in every test file, but perhaps it could be generalized):

const projectId = 'some-project-id';
const app = firebase.initializeAdminApp({ projectId });
const firestore = app.firestore();

If I recall correctly, and nothing has changed since, an annoying quirk here is that projectId has to correspond to a real project id. It's been some time since I last dug into this, but I believe that the functions emulator won't be setup properly, since I believe it falls back to calling real functions if emulation fails, which requires an actual project. This makes basically no sense and may have changed, so take it with a grain of salt, but I've always defaulted to using the ID of my dev project just in case.

I'm using Jest, so cleaning up within each describe block is pretty simple:

beforeEach(async () => {
  await firebase.clearFirestoreData({ projectId });
});

afterAll(async () => {
  Promise.all(firebase.apps().map((firebaseApp) => firebaseApp.delete()));
});

I suppose you don't necessarily have to clear the data between tests, but I prefer my tests to be performed in complete isolation whenever possible.

After this, each test basically does the following:

  1. Seed the database with any necessary test data. This is simply done by writing directly to the firestore instance defined earlier.
  2. When the database has been seeded, perform the creation/deletion/update you wish to test.
  3. Use wait-for-expect in combination with reading the firestore data to validate that the background function works as intended.
tettoffensive commented 3 years ago

So is anybody successfully testing firestore? (I'm currently seeing the error OP mentions). I've also tried getting it setup using the emulator in the past but never could get it quite working. I've been unable to find any sample projects for how to test. (TBH, I'm really surprised how hard it is to test with firebase).

FYI, @firebase/testing has been deprecated and they say to use @firebase/rules-unit-testing but that doesn't make sense as we're not wanting to unit testing rules.