Using Firebase Authentication in Tests with the Firebase Emulators

Image by Couleur on Pixabay

Firebase is a platform to build apps without managing infrastructure. To support testing Firebase offers Firebase emulators to test the functionality locally. On our own machine. Without the need to connect to the real Firebase services.

Using the emulator makes our tests run a lot faster. Something which is paramount when doing Test Driven Development.

The emulator works fine for Cloud Firestore, their database offering, and Cloud Functions, which allows to run backend functions without managing servers.

Not yet for Firebase Authentication though.

Firebase Authentication, for example, allows authentication via phone number.

We enter a phone number, get a verification code via SMS, enter the verification code and are authenticated.

Let’s say we want to find the uid of a user by phone number in one of our services for some reason. We can use Firebase Auth.

But if we have the code to get the user by its phone number called during the emulator tests using firebase-admin

It yields an error about our missing Firebase credentials.

Of course, we did not set them up, we were attempting to run against an emulator on our own machine.

We would need to set up a connection to the real services as described in the official docs.

Ok. Seems we need to have some valid credentials during testing then?

Not necessarily. We can take a different approach. Let’s first wrap the call to using the firebase-admin module.

In our service using the functionality, we replace the direct call, using firebase-admin, with the wrapping function above

Which allows us to replace the module getUserByPhonenumberwith a mock during testing. A mock would be an implementation that acts as a stand-in for the actual function during testing. For example an implementation that always returns the same phone number without ever accessing the real Firebase or the emulator.

This is possible because whatever service wants to use the phone number does not care how getUserByPhonenumber works under the hood. The service expects that the real implementation of getUserByPhonenumber is tested in the tests for the module.

Using the mocking library testdouble.js we create the following setup in our tests.

Important to remember is the require of our service under test, in our case service.js, needs to happen after testdouble has replaced the real implementation of getUserWithPhonenumber. The code in line 6 above needs to be after the code in line 5.

If we switched the order then service would get the real implementation making us wonder why all our mocking does not happen.

In our tests we can now tell the mock what to return when it is called.

Thus when our service under test will call getUserWithPhonenumber with exactly the phone number "123123123123" he will get the stubbed existingUser. No Firebase auth will be involved.

One might wonder why we are not mocking the Firebase Auth module directly. Why create the abstraction? It boils down to the mantra

Don’t Mock What You Don’t Own

We don’t own the third party dependency firebase-admin . We have no direct influence on its design choices. They might decide to change their API in the future, which would force us to change the behaviour of our mock. What is now getUserByPhoneNumber might in a future version become getAccountByPhone taking different arguments and return a different object. We don’t know yet.

There is an additional benefit of wrapping the call to Firebase Auth besides testing. Wrapping the function makes it easier to replace the functionality if we were to replace Firebase Auth with something different later and by wrapping all the Firebase Auth functionality we are using in such a service we create one single spot where we can see which functions of Firebase Auth are in use. Giving us an overview on what functionality the replacement would need to provide or what we would have to implement ourselves.

The question arises if the real admin.auth.getUserByPhoneNumber is ever tested? Yes it should be. But not in the service where we are testing business logic.

We can always create an end to end test which would use a real login and then does something a user would do in our application. For example log in in, search for a product and then buying it.

We could set this up with tools such as cypress.

Building things. Usually by writing code. www.hodler.co. Software Engineering @porschedigital

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store