While using the Google Cloud's Firestore Emulator, I'm calling the following method:
global.db.runTransaction(async () => 100)
This dummy call works when executed using node
, but fails when executed using jest
inside a test
function. When running it from Jest, the runTransaction
method throws:
Error: You must return a Promise in your transaction()-callback.
referencing node_modules/@google-cloud/firestore/build/src/transaction.js:362:27
That file has the following snippet of code, which is causing the failure:
async runTransaction(updateFunction, options) {
// ...
const promise = updateFunction(this);
if (!(promise instanceof Promise)) {
throw new Error('You must return a Promise in your transaction()-callback.');
}
// ...
}
In other words, when running in Jest, the library considers async () => 100
function not to return a promise.
I changed the library code to add some debug messages:
async runTransaction(updateFunction, options) {
// ...
const promise = updateFunction(this);
if (!(promise instanceof Promise)) {
// *** Added these debug messages ***
console.log(updateFunction);
console.log(promise);
console.log(promise instanceof Promise);
throw new Error('You must return a Promise in your transaction()-callback.');
}
// ...
}
And I am getting the following:
[AsyncFunction (anonymous)]
Promise { 100 }
false
To complicate matters, this test passes as expected within jest
:
test('promise', () => {
const fn = async () => 100;
const res = fn();
expect(res instanceof Promise).toEqual(true);
}
I'm a bit at a loss here as to why executing this library code with Jest fails compared to Node, while executing a similar statement with Jest in the code snippet just above succeeds.
Any suggestions on how to approach this head-scratcher?
EDIT
Initializing a local db
variable instead of global.db
solves the issue.
Originally, I had the global.db
set in jest.config.js
like so:
module.exports = async () => {
const Firestore = require("@google-cloud/firestore");
global.db = new Firestore({ projectId: process.env.GOOGLE_CLOUD_PROJECT });
return {}
}
The application code works by relying on global.db
that is set through an initialization file, and I wanted to mimic this for the tests.
I guess the question is, then, how should one go about initializing a global db variable that can be used in all tests?
ANSWER
Ended up creating a setup.js
file and loaded it with setupFilesAfterEnv
per the docs. Content:
const Firestore = require("@google-cloud/firestore/build/src");
global.db = new Firestore({ projectId: process.env.GOOGLE_CLOUD_PROJECT });
Now things are working properly. Other solutions, such as using the globals
entry in the object exported in jest.config.js
(not shown in code snippets above) didn't work as per the documentation the values must be JSON-serializable, which is not the case for Firestore.