I am using
Node 14
firebase-functions-test: 0.2.3
firebase-admin: 9.6.0
firebase-functions: 3.13.2
firebase tools: 9.8.0
so I want to perform unit testing for my firestore trigger function using firebase cloud function, I read the steps from the documentation in here.
I want to perform unit test using Firebase Emulator. so I assume I will initialize the SDK in offline mode. the documentation said that
If you would like to write completely offline tests, you can
initialize the SDK without any parameters:
so I initialize it like this
import * as firebase from "firebase-functions-test";
const test = firebase();
const wrapped = test.wrap(myFunctions.onCreate);
// rest of my test code
but when I run the test, I have this error:
Error: Could not load the default credentials. Browse to
https://cloud.google.com/docs/authentication/getting-started for more
information.
so even though I will use Firebase emulator (offline), it seems I need to provide the credential, which is the step if online mode is used, like explained from documentation in here
so I initialize the SDK like this
import * as firebase from "firebase-functions-test";
const test = firebase({
databaseURL: "https://xxx-b843e.firebaseio.com",
projectId: "xxx-b843e",
}, "../../../service-account.json");
const wrapped = test.wrap(myFunctions.onCreate);
// rest of my test code
but when I run the test, I have another error
{"severity":"WARNING","message":"Warning, FIREBASE_CONFIG and
GCLOUD_PROJECT environment variables are missing. Initializing
firebase-admin will fail"}
Error: The file at ../../../service-account.json does not exist, or it
is not a file. ENOENT: no such file or directory, lstat
'/Users/xxx/Documents/service-account.json'
the service account json file doesn't exist? I believe I have set the path correctly like this
in fact, I use intellisense to guide me to service-account.json path
the service-account.json file is like the image below, i get it from firebase project overview --> project settings --> service accounts --> generate new private key
what should I do if I want to initialize firebase functions test SDK in Firebase emulator?
Explanation
Error: The file at ../../../service-account.json does not exist
That error means that the service-account.json file could not be found at runtime (when you started the emulator), because the relative path is incorrect.
in fact, I use intellisense to guide me to service-account.json path
The path that intellisense suggests may not be the correct relative to the directory the code is executing in. This is because typescript is compiled before it's run, usually from within a lib or dist folder, which means the relative path needs to be changed.
Solution 1
You can use an absolute path /my/absolutepath/to/service-account.json, this is a fast way to resolve this problem, but isn't very portable.
Solution 2
Experiment with different amounts of ../../ to find the exact relative path of the service-account.json. Try using:
../../service-account.json
../../../service-account.json
etc ... (until it works)
It's most likely only off by a few ../
To use the offline mode you need to stub API calls like admin.initializeApp(). See the example given at https://firebase.google.com/docs/functions/unit-testing#importing_your_functions
// If index.js calls admin.initializeApp at the top of the file,
// we need to stub it out before requiring index.js. This is because the
// functions will be executed as a part of the require process.
// Here we stub admin.initializeApp to be a dummy function that doesn't do anything.
adminInitStub = sinon.stub(admin, 'initializeApp');
// Now we can require index.js and save the exports inside a namespace called myFunctions.
myFunctions = require('../index');
To test against the emulator you need to the initialize the test SDK with an emulator URL. Your file not found error is probably due to a problem with resolving relative paths. Try providing an absolute path and see if that works.
Getting rid of the warning should be as simple as adding the following to your jest.preset.js (or whatever code runs before your unit tests run).
const test = require('firebase-functions-test')();
https://firebase.google.com/docs/functions/unit-testing#offline-mode
I've gone through all the setup steps to make calls to the Google Vision API from a Node.js App. Link to the guide: https://cloud.google.com/vision/docs/libraries#setting_up_authentication
I'm using the ImageAnnotatorClient from the #google-cloud/vision package to make some text detections.
At first, it looked like everything was set up correctly but I don't know why it only allows me to do one request.
Further requests will give me the following error:
Error: 7 PERMISSION_DENIED: Your application has authenticated using end user credentials from the Google Cloud SDK or Google Cloud Shell which are not supported by the vision.googleapis.com. We recommend configuring the billing/quota_project setting in gcloud or using a service account through the auth/impersonate_service_account setting. For more information about service accounts and how to use them in your application, see https://cloud.google.com/docs/authentication/
If I restart the Node app it again allows me to do one request to the Vision API but then the subsequent requests keep failing.
Here's my code which is almost the same as in the examples:
const vision = require('#google-cloud/vision');
// Creates a client
const client = new vision.ImageAnnotatorClient();
const detectText = async (imgPath) => {
// console.log(imgPath);
const [result] = await client.textDetection(imgPath);
const detections = result.textAnnotations;
return detections;
}
It is worth to mention that this works every time when I run the Node app in my local machine. The problem is happening on my Ubuntu Droplet from Digital Ocean.
Again, I set everything up as it is in the guides. Created a Service Account, downloaded the Service Account Key JSON file, set up the environment variable like this:
export GOOGLE_APPLICATION_CREDENTIALS="PATH-TO-JSON-FILE"
I'm also setting the environment variable in the .bashrc file.
What could I be missing? Before setting up everything from scratch and go through the whole process again I thought it would be good to ask for some help.
So I found the problem. In my case, it was a problem with PM2 not passing the system env variables to the Node app.
So I had everything set up correctly auth-wise but the Node app wasn't seeing the GOOGLE_APPLICATION_CREDENTIALS env var.
I deleted the PM2 process, created a new one and now it works.
Firebase has this piece of information here at https://firebase.google.com/docs/functions/locations:
Client-side location selection for callable functions Regarding the callable function, client callable setups should follow the same guidelines as HTTP functions. The client can also specify a region, and must do so if the function runs in any region other than us-central1.
To set regions on the client, specify the desired region at
initialization:
var functions = firebase.app().functions('us-central1');
I've been trying to find which node module firebase is referring to but I have had no luck.
I know that it is not 'firebase-admin' or 'firebase-functions' but thats about it.
Anyone have any ideas what this might be referring to?
Edit:
I have now also tried using this with the imports require('firebase') and require('firebase/app') (as suggested) but neither of those seem to work. I also tried generating the app with const app = firebase.initializeApp({}); and then running app.functions("region") or app().functions("region") but i keep receiving TypeErrors saying functions is not a function and app is not a function respectively.
firebase.app().functions('us-central1'); is a part of the firebase-js-sdk. With source code documented on GitHub
NPM component is #firebase/functions, that is documented on the npmjs.com.
However, it is not intended for standalone usage, and should be used along with package Firebase.
You can install it using:
$ npm i firebase
I get the error: "Failed to retrieve function source code" when I try and deploy a function.
This is all from the command line. I am using node 6.11.5 (but in the firebase-admin package.json file in the nodes folder it is says node 6.9.1 is used to download that). I am using firebase-admin#5.8.1 and firebase-functions#0.8.1.
This is the code in my index.js file that I am trying to deploy:
const functions = require('firebase-functions');
exports.helloWorld = functions.https.onRequest((request, response) => {
response.send("Hello from Firebase!");
});
I have also tried to deploy many different things.
Two interesting things:
- I used to be able to deploy any function without problem. This changed about a month ago and now every function I try gets this error. I can't remember making any change that would be related to this.
- Also I can deploy functions from my computer (with the exact same set up and firebase versions) to other projects in the same google account and different google accounts without any problem.
Thanks
I think you should check your billing settings in google cloud. I got the same problem and after updating billing information then redeploy the function, the error is gone.
Today Firebase released its brand new product Cloud Functions for Firebase and I just created a hello world function and deploy it on my existing firebase project.
It looks like it bundles all dependencies and upload it to firebase just like aws lambda function does. But it takes too much time to be done even on minor changes in code and also need a good connectivity of internet . If you are offline for some reason, you are just in dark what code you are writing until you have a way to execute and test that functions offline on your local machine.
Is there any way to test Cloud Functions for Firebase locally?
firebaser here
Deployment of your Functions indeed takes more time than what I'm normally willing to wait for. We're working hard to improve that and (as Brendan said) are working on a local emulator.
But for the moment, I mostly write my actual business logic into a separate Node script first. That way I can test it from a local command prompt with node speech.js. Once I'm satisfied that the function works, I either copy/paste it into my actual Functions file or (better) import the speech module into my functions file and invoke it from there.
One abbreviated example that I quickly dug up is when I was wiring up text extraction using the Cloud Vision API. I have a file called ocr.js that contains:
var fetch = require('node-fetch');
function extract_text(url, gcloud_authorization) {
console.log('extract_text from image '+url+' with authorization '+gcloud_authorization);
return fetch(url).then(function(res) {
return res.buffer();
}).then(function(buffer) {
return fetch('https://vision.googleapis.com/v1/images:annotate?key='+gcloud_authorization, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
"requests":[
{
"image":{
"content": buffer.toString('base64')
},
"features":[
{
"type":"TEXT_DETECTION",
"maxResults":1
}
]
}
]
})
});
}).then(function(res) {
var json = res.json();
if (res.status >= 200 && res.status < 300) {
return json;
} else {
return json.then(Promise.reject.bind(Promise));
}
}).then(function(json) {
if (json.responses && json.responses.length && json.responses[0].error) {
return Promise.reject(json.responses[0].error);
}
return json.responses[0].textAnnotations[0].description;
});
}
if (process.argv.length > 2) {
// by passing the image URL and gcloud access token, you can test this module
process.argv.forEach(a => console.log(a));
extract_text(
process.argv[2], // image URL
process.argv[3] // gcloud access token or API key
).then(function(description) {
console.log(description);
}).catch(function(error) {
console.error(error);
});
}
exports.extract_text = extract_text;
And then in my Functions index.js, I have:
var functions = require('firebase-functions');
var fetch = require('node-fetch');
var ocr = require('./ocr.js');
exports.ocr = functions.database().path('/messages/{room}/{id}').onWrite(function(event) {
console.log('OCR triggered for /messages/'+event.params.room+'/'+event.params.id);
if (!event.data || !event.data.exists()) return;
if (event.data.ocr) return;
if (event.data.val().text.indexOf("https://firebasestorage.googleapis.com/") !== 0) return; // only OCR images
console.log(JSON.stringify(functions.env));
return ocr.extract_text(event.data.val().text, functions.env.googlecloud.apikey).then(function(text) {
return event.data.adminRef.update({ ocr: text });
});
});
So as you can see this last file is really just about wiring up the "worker method" ocr.extract_text to the database location.
Note this is a project from a while ago, so some of the syntax (mostly the functions.env part) might have changed a bit.
firebaser here
To debug your Cloud Functions for Firebase locally, there is an emulator. See the documentation for more info.
run and debug/inspect functions locally
prerequisites (google-cloud functions and firebase-specific):
npm install -g #google-cloud/functions-emulator
npm install --save firebase-functions
npm install -g firebase-tools
To run and inspect/debug: first run functions locally, then inspect each function, and finally run each specific function to debug+inspect it. Use functions start as an alternative to firebase serve and note the documentation for each tool is available (and useful).
To run and debug the specific function myFn as-expected (eg in Nodejs via chrome://inspect and note this works using Nodejs v10 though not officially supported):
firebase serve --only functions
functions inspect myFn
functions call myFn # or call from browser
additional documentation:
https://firebase.google.com/docs/functions/local-emulator
https://cloud.google.com/functions/docs/emulator#debug-emulator
https://github.com/GoogleCloudPlatform/cloud-functions-emulator/wiki
>> Is there any way to test Cloud Functions for Firebase locally?
You can use the following command to start a firebase shell (execute in your functions directory):
npm run build && firebase functions:shell
You can invoke your functions in the shell like so:
helloWorld()
Refer this link for more information.
Answered here: https://github.com/firebase/firebase-functions/issues/4#issuecomment-286515989
Google Cloud Functions also open-sourced a local emulator, and we are
working to build a tighter integration with Cloud Functions for
Firebase. In the meanwhile, you can check it at here:
https://github.com/GoogleCloudPlatform/cloud-functions-emulator/
The emulator does allow you to run functions locally. Here's the
documentation that explains how to use it:
https://cloud.google.com/functions/docs/emulator
I couldn't get the single stepping working at first. My process was the same as documented in many answers here.
Also, these pages contain nearly all the documentation I required:
https://firebase.google.com/docs/functions/local-emulator
https://cloud.google.com/functions/docs/emulator#debugging_with_the_emulator
I had got the functions running using firebase serve --only functions, but hadn't got the debugger up and running. Then I came across the other way of directly using the emulator and managed to hit a break point like this:
# start the emulator
functions start
# allow inspection
functions inspect helloWorld
# call the function from the cli
functions call helloWorld
This worked, and I could hit a breakpoint.
However, when hitting the endpoint for the function in postman or the browser, I got no response at all.
The step I was missing was:
# deploy the function to the emulator
functions deploy helloWorld --trigger-http
# you need to toggle inspection after the deploy
functions inspect helloWorld
Now I can hit the endpoint for the function from postman or the browser, and the breakpoint is hit.
I recommend the brilliant NiM chrome extension for debugging and hope this answer helps someone, even if this is an old question.
Firstly, I suggest you to install following dependencies,
npm install --save firebase-functions
npm install -g firebase-tools
If already installed then you can update it to latest one. Generally, functions-emulator comes with above dependency but still I would recommend you to update it,
npm install -g #google-cloud/functions-emulator
Once it has been updated, go to functions folder of you application and run following command,
firebase serve --only functions
I hope it helps!
For vscode users debugging HTTP functions (webhooks, etc)...
The google cloud emulator (firebase serve --only functions) launches a separate process to run your functions. You can attach to this process with vscode, but since the emulator only creates this process after the first function is called, it's not straightforward.
create a dummy HTTP endpoint in your functions which will return the processID:
app.get("/processid", function(request, response) {
response.send(`${process.pid}`);
});
start the emulator with firebase serve --only functions
call the http://<localhost_url>/processid endpoint. This will create the process and return the processID
use vscode to attach to the specified process. You can now set breakpoints, step, etc on any of the other functions (they all use the same process).
There's probably a nicer way to glue all this together.
There is now a cloud functions emulator that lets you call functions locally
Once I have completed my PoC I will update this answer to include code and steps I used.