62
49

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ローカル環境でCloud Functions for Firebase(特にCallable関数)の動作テストをする

Last updated at Posted at 2020-01-28

functionsって便利なのですが、真面目に開発してるとデプロイの待ち時間が苦痛になってきます。
ローカルである程度動作テストしてからDeployしたいものです。また、チェック用のコードを書くのもめんどくさいときはインタラクティブShellが利用できるので試してみます。

前提

  • 既にfirebaseのプロジェクトはある状態
  • Firestoreとか認証とかはサーバ側のものを利用する(デプロイの時短がメインの目的なので)
  • ここで言うテストとは単体テストとかじゃなく動作テスト

基本

まず、普通のWebAPIの場合を見てみます。

準備

作業場所を作り、firebase initの後、functionsの中で作業します。

mkdir functions-test
cd functions-test

firebase init functions

...省略

cd functions

私は既存プロジェクトを選んで、言語はJSです。

テンプレートのhelloWorldをコメントインし実行

index.jsにテンプレートとして記述されているhelloWorldをコメントインして利用してみます。

index.js
const functions = require('firebase-functions');

exports.helloWorld = functions.https.onRequest((request, response) => {
 response.send("Hello from Firebase!");
});

ローカルで実行してみます。

firebase serve --only functions

ローカルに展開され、実行用のURLが表示されます。

✔  functions: Emulator started at http://localhost:5000
i  functions: Watching "/Users/tamaki/func-test/functions" for Cloud Functions...
✔  functions[helloWorld]: http function initialized (http://localhost:5000/[project-name]/us-central1/helloWorld).
i  functions: Beginning execution of "helloWorld"
i  functions: Finished "helloWorld" in ~1s

WebAPIなのでブラウザ等に上記URLを入力すれば実行されます。

http://localhost:5000/[project-name]/us-central1/helloWorld

まあ、簡単ですが、WebAPIを何の認証もDB連携もなく実行することはないため、上記だけで動作確認できるケースはあまり多くないです。

onCall()の展開と呼び出し

Firebaseでhttps経由でAPIを呼び出す場合、Callable関数を利用することの方が多いかと思います。
index.jsを書き換えてonCall()関数を記述します。

サーバ側

ただ、Hello OnCall!と返すだけの関数を記述します。

index.js
const functions = require('firebase-functions');

exports.helloOnCall = functions.https.onCall((data, context) => {
    return "Hello OnCall!";
});

記述して保存すると自動展開されます(watching状態なら)。
一度、serveを停止している場合は、再びfirebase serveします。

✔  functions[helloOnCall]: http function initialized (http://localhost:5000/test-fc01e/us-central1/helloOnCall).

クライアント側

では、APIを呼び出すクライアント作成してみます。作業場所はfunctionsの中を想定しています。
firebaseの機能を利用するためfirebaseをインストールします。また、呼び出しコードを記述するcallFunciton.jsを作成します。

npm install --save firebase
touch callFunction.js

実装は下記のような感じ。ポイントは、

functions.useFunctionsEmulator("http://localhost:5000");

と記述することでローカルのFunctionsを見るようにします。

callFunction.js
const firebase = require("firebase");

const firebaseConfig = {
    apiKey: "xxxxxxxxxxxxxxxxxxxxxx",
    authDomain: "xxxxxxxxx.firebaseapp.com",
    databaseURL: "https://xxxxxxxxx.firebaseio.com",
    projectId: "xxxxxxxxx",
    storageBucket: "xxxxxxxxxx.appspot.com",
    messagingSenderId: "xxxxxxxxxxxx",
    appId: "xxxxxxxxxxxxxxxxxxxxxxxxxx",
    measurementId: "xxxxxxxxxxxxxx"
};

firebase.initializeApp(firebaseConfig);
const functions = firebase.functions();
functions.useFunctionsEmulator("http://localhost:5000");

const main = async () => {

    const helloOnCall = functions.httpsCallable("helloOnCall");
    const res = await helloOnCall({});
    console.log(res);

}

main();

実行してみます。

当然ですがFunctionsがserve状態である必要があります。

node callFunction.js

{ data: 'Hello OnCall!' }

応用1:Firestoreを利用する関数の利用

多くの場合、Functionsの処理ではDB(firestore)を利用することになりますが、その場合はどうしたらいいでしょうか。
連携パターンとしては下記が考えられます。

  1. ローカルのFunctionからローカルのFirestoreを利用する
  2. ローカルのFunctionからFirebaseのFirestoreを利用する

(私なりの)結論から言えば、1.はあまりメリットが無いため、2.の想定した利用方法を見てみます(ローカルを利用することはできます)。
なお、ローカルでFunctions, Firestoreを利用する方法はこちらで簡単に書いています。

下記ではitemsコレクションにデータを書き込み、続いて、レコード件数を返す機能をサンプル実装しています。
1つ注意があるとすれば、ローカルのFunctionからfirestoreを利用するためには(普通にnodeスクリプトを書くのと同様に)認証情報を記述する必要があることです。

サーバ側

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

//ローカルで実行するには認証情報が必要
admin.initializeApp({
    credential: admin.credential.cert("/path/to/key.json"),
    databaseURL: "https://test-fc01e.firebaseio.com"
});

const db = admin.firestore();

//ローカルfirestoreを使う場合
// db.settings({
//     host: "localhost:8080",
//     ssl: false,
// });

exports.helloOnCall = functions.https.onCall(async (data, context) => {

    //write
    const res = await db.collection("items").add({ name: "hoge" });

    //read
    const snapshots = await db.collection("items").get();
    const docs = snapshots.docs.map(doc => doc.data());
    return docs.length;

});

上記ではコード中に認証情報を記述していますが、

export GOOGLE_APPLICATION_CREDENTIALS="/path/to/key.json"

とすることで環境変数に設定することもできます。サーバ側にソースを合わせるなら環境変数の方がいいかもしれません。

削除はunset GOOGLE_APPLICATION_CREDENTIALSとする。

クライアント側

callFunction.jsはそのままで実行してみます。

node callFunction.js

{ data: 1 }

応用2:インタラクティブShellの利用

いちいちテスト用のコードを書くのがめんどくさいときがあります。そんな時はインタラクティブShellを利用することができます。

下記のように、firebase functions:shellとすると、インタラクティブShellが起動します。

firebase functions:shell

✔  functions: Emulator started at http://localhost:5000
i  functions: Loaded functions: helloWorld, helloOnCall
firebase > 

ここで、onRequest関数を実行すると、下記のようになります。

firebase > helloWorld();
Sent request to function.
firebase > 
RESPONSE RECEIVED FROM FUNCTION: 200, hello

また、onCall()関数においてもインタラクティブにテストすることができます。

firebase > helloOnCall({});
Sent request to function.
firebase > 
RESPONSE RECEIVED FROM FUNCTION: 200, {
  "result": 5
}

便利。

62
49
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
62
49

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?