Testing Functions Locally with the Cloud Functions Shell

If you’ve been working with Cloud Functions for Firebase, you’ve probably wondered how you could speed up the development of your functions. This is possible for HTTPS type functions using the firebase serve in the Firebase CLI, but this wasn’t an option for other types of functions. Now, local testing of all of your functions is easy with the Firebase CLI. If you want to try out your code before you deploy it to Cloud Functions, you can do that with the Cloud Functions shell in the Firebase CLI starting at version 3.11.0 or later.

Here’s how it works, in a nutshell. We’ll use a Realtime Database trigger as an example.

Imagine you have an existing project with a single function in it called makeUppercase. It doesn’t have to be deployed yet, just defined in your index.js:

exports.makeUppercase = functions.database.ref('/messages/{pushId}/original').onCreate(event => {
    const original = event.data.val()
    console.log('Uppercasing', event.params.pushId, original)
    const uppercase = original.toUpperCase()
    return event.data.ref.parent.child('uppercase').set(uppercase)
})

This onCreate database trigger runs when a new message is pushed under /messages with a child called original, and writes back to that message a new child called uppercased with the original value capitalized.

Now, if you can kick off the emulator shell from your command line using the Firebase CLI:

$ cd your_project_dir
$ firebase experimental:functions:shell

Then, you’ll see something like this:

i  functions: Preparing to emulate functions.
✔  functions: makeUppercase
firebase> 

That firebase prompt is waiting there for you to issue some commands to invoke your makeUppercase function. The documentation for testing database triggers says that you can use the following syntax to invoke the function with incoming data to describe the event:

makeUppercase('foo')

This emulates the trigger of an event that would be generated when a new message object is pushed under /messages that has a child named original with the string value “foo”. When you run this command in the shell, it will generate some output at the console like this:

info: User function triggered, starting execution
info: Uppercasing pushId1 foo
info: Execution took 892 ms, user function completed successfully

Notice that the console log in the function is printed, and it shows that the database path wildcard pushId was automatically assigned the value pushId1 for you. Very convenient! But you can still specify the wildcard values yourself, if you prefer:

makeUppercase('foo', {params: {pushId: 'custom_push_id'}})

After emulating this function, if you look inside the database, you should also see the results of the function on display, with /messages/{pushId}/uppercased set to the uppercased string string value “FOO”.

You can simulate any database event this way (onCreate, onDelete, onUpdate, onWrite). Be sure to read the docs to learn how to invoke them each correctly.

In addition to database triggers, you can also emulate HTTPS functions, PubSub functions, Analytics functions, Storage functions, and Auth functions, each with their own special syntax.

The Cloud Functions shell is currently an experimental offering, and as such, you may experience some rough edges. If you encounter a problem, please let us know by filing a bug report. You can also talk to other Cloud Functions users on the Firebase Slack in the #functions channel.

Some tips for using the shell

Typing the function invocation each time can be kind of a pain, so be sure to take advantage of the fact that you can navigate and repurpose your invocation history much like you would your shell’s command line using the arrow keys.

Also note that the shell is actually a full node REPL that you can use to execute arbitrary JavaScript code and use special REPL commands and keys. This can be useful for scripting some of your test code.

Since you can execute arbitrary code, you can also dynamically load and execute code from other files using the require() function that you’re probably already familiar with.

And lastly, if you’re like me, and you prefer to use a programmer’s editor such as VS Code to write your all JavaScript, you can easily emulate functions by sending code you want to run to the Firebase CLI. This command will run test code from a file redirected through standard input:

$ firebase experimental:functions:shell < tests.js

Happy testing!