Testing Firebase Cloud Functions with Jest

Gompro
Gompro
Dec 24, 2018 · 5 min read

2019 is coming and 2019 would be the year of “Serverless”. Popularity of Serverless has grown rapidly. However, it’s hard to properly setup and test functions due to lack of resources. Of course, there are lots of articles for ‘How to start’ but not much for testing. I believe many of you already know how much benefit TDD provides you with. So I’m not gonna repeat it here. In this article, I’m gonna tell you how to setup your Firebase cloud functions and write unit test for them.

Before We Start

I assume you already have at least one function. If you’re not, then follow this official tutorial to create a new project(https://firebase.google.com/docs/functions/get-started)

Getting Started

Let me briefly explain about the function we’re going to create.

I’ll create some trigger for user.onCreate method. (you can check what it is from https://firebase.google.com/docs/functions/auth-events) This trigger function will save a newly authenticated user into our realtime database with several necessary properties for my simple game app. First, look at the code below.

Ok, check out what it is in detail. First, onCreate method accepts one parameter, user. User is an object returned after authentication via email/password, anonymous auth, 3rd party auth providers, etc. We can use this parameter to add new properties like above. As I’m making a game app, I added properties like points and quest related ones. Then I called admin.database().ref(path).set method to save our newly created user into database. (Exactly same process as using database with firebase SDK)

Simple enough, right?

However, if you try to test these trigger functions, then it will become a huge pain. Because it is hard to setup client apps and manually iterate auth process.(Click on auth button — type in email/password — wait for auth to finish — go to console and check our user is saved or not)

Write Test

That’s where automated test shines. You can repeat testing for many times without those hassles. All you have to do is just type test commands (yarn test) and iterate through “red/green” light process. So let’s write a test for Firebase cloud functions. There are some example codes for this(https://github.com/firebase/functions-samples/blob/master/quickstarts/uppercase/functions/test/test.offline.js) but this code sample uses Mocha and Chai. As a lover of Jest(https://jestjs.io/), I wanted to use Jest for my tests.

Ok, let’s setup jest into your project. I prefer to use Typescript for my projects so this setup may be little bit different if you use plain javascript, but there will not be great difference.

  1. Add jest.config.js under your functions directory.

2. Create __tests__ directory under src and add basic test file.

it("should pass", () => {
// meaningless test
expect(1).toBe(1);
});

3. Download Service-key from firebase console.

Go to settings and click on service account tab. You’ll find create new account key button below. Download this key and include in your project. (Don’t forget to include this file in your .gitignore file — keep your secrets safe!)

4. Add test command in your package.json file.

"scripts": {...other commands
// I prefer to set verbose=false, because it shows console.logs in terminal window. You can remove this flag.
"test": "jest --watchAll --verbose=false"},

5. Install dependencies.

yarn add jest @types/jest firebase-functions-test ts-jest -D

Now, you’re good to go.

Write our test code to test our trigger function.

As we’re going to use realtime database(save user), we have to go online. It will require us to specify projectConfig and service-key.json. If you followed my instructions above, then you must have service-key.json file in your project directory. Then how you can check projectId and databaseURL?

For databaseURL, go to database tab and create new realtime database, then you should see your databaseURL like that.

And for projectId, go to settings/common.

Ok, let’s write more code.

Ooooh, lots of things going on here!

I’ll explain code step by step. First thing you can see is beforeAll block. I used jest.spyOn method to stub our admin.initializeApp method. Then I loaded our functions. (You have to stub admin method to provide our admin with proper credentials) And in afterAll block, we clean up the setups.

To execute our function in right environment, we use wrap method of testEnv. By doing this, we are able to invoke user create event in our test just by calling wrapped(testUser). After invocation, we read our user from the database and check this user has property named points and its value is 0 or not.

And that’s all! You can add another tests from now on. Now we’re done with testing non-http function triggers. We still have to test for http functions, however, testing http function is not that different from testing triggers. All you have to change is that instead of passing custom parameter into wrapped function, you pass req and res object instead.

// code from https://github.com/firebase/functions-samples/blob/master/quickstarts/uppercase/functions/test/test.offline.js
const req = { query: {text: 'input'} };
const res = {
redirect: (code, url) => {
assert.equal(code, 303);
assert.equal(url, 'new_ref');
done();
}
};
myFunctions.addMessage(req, res);

Write Test (Offline)

There is another option you can choose when you test your cloud functions. It is “offline” mode. This one is quicker (because you don’t have to deal with database transactions) and easy to write.

In offline mode testing, we have to stub our admin SDK so that it doesn’t require credentials. Let’s dive into the code!

You can see that I used jest.mock method. First I declared our mock “firebase-admin” (you should give module name as parameter, not a module object itself) and then give it partial implementation. (initializeApp and database method) After this mock setup, all we have to check is the return value of our trigger function which is true and the parameter which our mockSet function takes which will be our newUser object.

So our test code should look like this.

It seems that these mocking test is useless. I just repeated my function implementation again. However it becomes much valuable when our function gets larger or includes complex if-else logic inside. You can easily test each cases with these mocking tests. (It becomes especially valuable when you have to deal with edge cases that can’t be easily tested manually)


Testing gives you great freedom. You can repeat as many times you can quickly with automated tests. Go write your own test and deploy your functions with confidence! Thanks for reading my article.

Gompro

Written by

Gompro

Has experience in Node.js | React.js | Redux | Typescript | Android

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade