Testing event handlers for a slack bolt app has been quite the rollercoaster. Initially, all tests passed flawlessly, making life wonderful. However, after some refactoring, the entire test suite failed to run, displaying an error indicating that firebase initialization had failed.
The reason behind firebase failing to initialize lies in jest lacking the necessary environment variables required for proper firebase setup. Oddly enough, I do not want it to initialize at all. Strangely, it did not exhibit this behavior in the previous test runs. Thus, I am left pondering why it's attempting to initialize firebase during test runs now when it wasn't before.
The most basic test within Events.test.ts
checks the handleBlockActions
function in Events.ts
, which simply acknowledges a slack event. Everything works fine until another function, updateOrganizationName
from Event.ts
, is called to make a database query. Even though I am not importing handleTeamRename
into Events.test.ts
, just having it present in Events.ts
causes the test to fail with the firebase related message shown below. Interestingly, if I remove the call to updateOrganizationName
, the test passes without issue.
The puzzling part is how merely having a function within an imported file that makes a DB call can cause my test to fail due to firebase being improperly initialized.
I have endeavored to create a minimal reproducible example; while the original files are more complex, I have stripped them down to their essence to pinpoint the issue using the smallest possible codebase.
I would greatly appreciate any insights into understanding jest's actions, why it insists on initializing firebase despite seemingly no direct request from my end, and how to navigate this scenario more effectively.
Most solutions available online pertain to mocking firebase broadly, but I wish to avoid that avenue currently considering it worked previously. Is there a way around it?
file structure
- database
- Organizations.ts
- Slack.ts
- firebase.ts
- services
- Events.test.ts
- Events.ts
// Events.ts
import { AckFn, DialogValidation, SayArguments } from "@slack/bolt";
import { updateOrganizationName } from "../database/Organizations";
import { getSlackTeam } from "../database/Slack";
export type AckFnSig =
| AckFn<void>
| AckFn<string | SayArguments>
| AckFn<DialogValidation>;
/**
* A user renames the organization name in slack
*/
export const handleTeamRename = async (
userId: string,
token: string
): Promise<void> => {
try {
const team = await getSlackTeam(token, userId);
// if I comment out this line, the test passes
await updateOrganizationName(team.id, team.name, team.icon.image_230);
} catch (e) {
console.error("team_rename event");
console.error(e);
}
};
/**
* Acknowledge user clicking on action button
*/
export const handleBlockActions = async (ack: AckFnSig): Promise<void> => {
try {
ack();
} catch (e) {
console.error("action_link_accounts event");
console.error(e);
}
};
// Events.test.ts
import { AckFnSig, handleBlockActions } from "./Events";
describe("events", () => {
describe("handleBlockActions", () => {
it("should ack block actions", async () => {
const mockAck = jest.fn();
void (await handleBlockActions(
(mockAck as unknown) as AckFnSig
));
await expect(mockAck).toHaveBeenCalledTimes(1);
});
});
});
FAIL src/services/Events.test.ts
● Test suite failed to run
Service account object must contain a string "project_id" property.
8 | admin.initializeApp({
9 | // current required type appears to be wrong so has to be cast as any
> 10 | credential: admin.credential.cert(firebaseServiceKey as any),
| ^
11 | databaseURL:
12 | process.env.FIREBASE_URL || "[my firebase url]",
13 | });
at FirebaseAppError.FirebaseError [as constructor] (node_modules/firebase-admin/lib/utils/error.js:44:28)
at FirebaseAppError.PrefixedFirebaseError [as constructor] (node_modules/firebase-admin/lib/utils/error.js:90:28)
at new FirebaseAppError (node_modules/firebase-admin/lib/utils/error.js:125:28)
at new ServiceAccount (node_modules/firebase-admin/lib/credential/credential-internal.js:134:19)
at new ServiceAccountCredential (node_modules/firebase-admin/lib/credential/credential-internal.js:68:15)
at Object.<anonymous>.exports.cert (node_modules/firebase-admin/lib/credential/credential.js:34:54)
at Object.<anonymous> (src/database/firebase.ts:10:32)