Unit Testing of Cloud Functions

Sometimes you may want to test your cloud functions locally, without deploying. This can be useful for unit testing or for debugging during development. This page describes recommended best practices for local unit testing.

Invoking functions locally

To invoke your functions locally, create a file named testing.js in the /functions/test/ directory and import index.js as a module:

var myFunctions = require('../index');

Each of your functions is exported as a property on the required module, and it is just a JavaScript function. So if you had a function named "doSomething", you could call it with myFunctions.doSomething().

This page illustrates how to create unit tests by using the test framework Mocha for two sample functions from the tutorial Create and Deploy Your First Cloud Functions: the HTTPS function addMessage and the Realtime Database function makeUppercase.

Invoking non-HTTPS functions

All cloud functions, except for HTTPS functions, take an Event object as a parameter. An Event is a JavaScript object containing all of the information about the triggered event, including any data payload. The data property for the event looks different, depending on the type of trigger. For database functions like makeUpperCase, the data property is a DeltaSnapshot, which needs to be constructed in the fake event. To stub a database write event for the makeUpperCase function, the code might look like this:

To invoke makeUpperCase with this stub event, do the following:

myFunctions.makeUppercase(fakeEvent);

All non-HTTPS cloud functions return a promise, even if your code does not. Upon successful completion, the promise resolves with the return value of your function. You can make assertions about the expected return value using chai-as-promised.

return assert.eventually.equal(myFunctions.makeUppercase(fakeEvent), expectedVal);

If you try to invoke makeUppercase at this point, you get an error message. This is because makeUppercase tried to write to the Firebase Realtime Database. If your function makes any writes to the database, you can mock a database write.

Invoking HTTPS functions

An HTTPS-triggered cloud function takes two parameters: a request object and a response object. Here is how you might test the addMessage example function: Override the redirect function in the response object, since sendMessage calls it. Within the redirect function, use chai.assert to help make assertions about what the parameters the redirect function should be called with:

If you try to run the above code, you get an error message. This is because sendMessage tried to write to the Firebase Realtime Database. If your function makes any writes to the database, you can mock a database write.

All HTTPS-triggered cloud functions are also Express apps, with the same request and response object types, so you can make use of libraries like Supertest and MockExpress in your testing as well.

Mocking functions.config() and admin.initializeApp()

If your code uses Runtime Config values, it is necessary to mock functions.config() to test your cloud functions locally.

In the source code for makeUppercase and sendMessage, note these lines at the top of the file:

// index.js
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

To avoid getting an error from initializeApp, you must stub the Firebase config values as well as admin.initializeApp. A great place to do this is in the "before" function of your Mocha tests, which gets executed before any tests are run. You can then restore these stubs in the "after" function.

In test.js:

Mocking database writes

If you mock functions.config() with a test app's real Firebase configuration values, any writes to the database that your function makes actually get executed against the real database. This may be useful for debugging. However, if you would like write unit tests that are isolated and have no side effects, you should mock database writes.

Mocking writes to event.data.ref

To get started mocking database writes, it's useful to analyze a function's code and ensure that you mock all the necessary components. For example, to analyze makeUpperCase, do the following:

return event.data.ref.parent.child('uppercase').set(uppercase);

This line is a chaining of a series of properties and functions:

  • event: you need to fake an event object, which was covered in Invoking non-HTTPS functions.
  • data: the fake event needs to have a data field.
  • ref: this is a getter function, so it needs to be stubbed with refStub.
  • parent: this is a property, not a function, so you need to ensure that refStub returns an object which has a 'parent' property.
  • child: this is a function that will be stubbed with childStub. Sinon has the ability to specify that a function should only be stubbed if it is called with particular arguments. In this case, you want to only stub the child function if it is called with 'uppercase' as a parameter.
  • set: this is a function that will be stubbed with setStub. The function is expected to be called with the capitalized word. 'INPUT'. Because this is the last function in the chain, you can set a return value for the stub function, and then check for this later to verify that the function has executed correctly.

Since the final function, 'set', was stubbed to return true, you can check for this return value as a way to verify that this whole chain of stubs were called properly.

Mocking writes to the admin app's database

Mocking writes to the admin app's database is very similar to mocking writes to event.data.ref. First, examine how the cloud function code does the write, then create stubs for each function call in the chain.

This is how addMessage is doing the database write`:

admin.database().ref('/messages').push({original: original}).then(snapshot => { res.redirect(303, snapshot.ref); });

Notice here that push is supposed to return a promise, which resolves with a snapshot value that has a ref property. It's important to ensure that the stub returns this, so that res.direct can work properly.

Review Complete Sample Test Code

You can view the complete unit tests for makeUppercase and sendMessage as well as the original functions on our GitHub repository.

Send feedback about...

Need help? Visit our support page.