LoginSignup
5
2

More than 1 year has passed since last update.

firebase emulator + Python

Posted at

Firebase emulator がリリースされていて とても開発がはかどりました。
まだ情報がすくないのでPythonからの利用方法をまとめます。

Firebase emulator

firestore をメインのDBとして使用しているので、単体テストで色々なテストデータを登録して作成するのに、ローカルでemulator が動かせると便利です。

下記のような仕組みが準備されているので、まずローカルにemulatorをInstallする

開発言語自体はPythonを使いたいのですが、emulatorは node, javaで動いているので事前にインストールしておく必要があります。

% node --version
v16.13.2
% npm install -g firebase-tools

% firebase login
Already logged in as {$USER}
  • firebaseのプロジェクトを選択時に下記のようなエラーがでることがあります

    % firebase projects:list
    ✖ Preparing the list of your Firebase projects
    
    Error: Failed to list Firebase projects. See firebase-debug.log for more info.
    

    表示されているとおりfirebase-debug.log を開いてみると下記のError表示

    Error: HTTP Error: 401, Request had invalid authentication credentials. Expected OAuth 2 access token
    

    login できているって表示されてたのに、なぜ!!と思いながら上記のエラーメッセージでググると下記のパラメータで強制的に再認証シーケンスを走らせることで対応できるとのことです。
    shell
    % firebase login --reauth --no-localhost

  • emulatorの設定
    shell
    firebase init emulators

  • emulator実行

% cd firebase_emulator 
% firebase emulators:start
i  emulators: Starting emulators: firestore, storage
i  firestore: Firestore Emulator logging to firestore-debug.log
i  ui: Emulator UI logging to ui-debug.log

┌─────────────────────────────────────────────────────────────┐
│ ✔  All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://localhost:4000                │
└─────────────────────────────────────────────────────────────┘

┌───────────┬────────────────┬─────────────────────────────────┐
│ Emulator  │ Host:Port      │ View in Emulator UI             │
├───────────┼────────────────┼─────────────────────────────────┤
│ Firestore │ localhost:8090 │ http://localhost:4000/firestore │
├───────────┼────────────────┼─────────────────────────────────┤
│ Storage   │ localhost:9199 │ http://localhost:4000/storage   │
└───────────┴────────────────┴─────────────────────────────────┘
  Emulator Hub running at localhost:4400
  Other reserved ports: 4500

Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.

python からemulatorに接続する

以下のように OSの環境変数にlocal emulator に設定することで
通常通りfirebase admin sdk を使用すると アクセス先がローカルのfirebase emulator になる

  • この情報が公式手順上明記されておらず最初はできないのかとおもいました。
    • まだemulator 自体が、βバージョンということなので今後のドキュメント含めた拡張に期待します
  • 注意点として本番のfirestoreを誤って上書きしてしまわにように、テストのときのProject,ルートドキュメントを別の名前にしておいたほうが良いと思います。
  • Localや、GCP上でインスタンスが生成されたままだとfirebase_admin.initialize_appで既にあるとエラーになります。下記のように_appsのlenを調べて初期化必要かどうかを判定しています
import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore

os.environ["FIRESTORE_EMULATOR_HOST"]="localhost:8090"
os.environ["GCLOUD_PROJECT"]="my_project"

cred = credentials.Certificate(self.config['firebase']['certjson'])
if (not len(firebase_admin._apps)):
    firebase_admin.initialize_app(cred)
self.db = firestore.client()

pytest

例えば、pytest上で、emulator をつかって、テスト条件を一定にするために最初にdocument, collection をクリアにするというようなコードは以下のように書くことができます。(emulatorでも普通のfirestoreの使い方と変わりません。)

    def setup_method(self,method):
        print('method={}'.format(method.__name__))
        delete_collection(self.db.collection(u'hoge').document(u'fuga1').collection(u'foo'),16)
        delete_collection(self.db.collection(u'hoge').document(u'fuga2').collection(u'baa'),16)
        doc_ref = self.db.collection(u'hoge').document(u'fuga1')
        doc_ref.set({
            u'key1': False,
            u'key2': True
        })
        doc_ref = self.db.collection(u'hoge').document(u'fuga2')
        doc_ref.set({
            u'key1' : False,
            u'key2' : True
        })
  • Test例: 以下は、ab, cd というデータが事前に登録されていた場合正しく収集できるかという関数のテスト例です。
    def test_idea_collection(self):
        col_ref = self.db.collection(u'hoge').document(u'fuga1').collection(u'foo')
        timestr1 = '2022/2/3 16:48:11'
        time1 = datetime.strptime(timestr1 + '+0900', '%Y/%m/%d %H:%M:%S%z')
        timestr2 = '2022/2/6 16:48:11'
        time2 = datetime.strptime(timestr2 + '+0900', '%Y/%m/%d %H:%M:%S%z')
        col_ref.add({
            u'author': 'x-man',
            u'content':'ab',
            u'createdAt':timestr1
        })
        col_ref.add({
            u'author': u'x-woman',
            u'content':'cd',
            u'createdAt':timestr1
        })
        collectresult,lastupdate = self.main_target.collect_foo('fuga1')
        assert collectresult.count('ab') == 1
        assert collectresult.count('cd') == 1
        assert collectresult.count('ef') == 0
  • test 実行例
    • emulator, pytestがローカルでInstallされていれば以下のようにテスト実行を確認できます
% pytest test_hogehoge.py 
========================================================================= test session starts =========================================================================
platform darwin -- Python 3.9.0, pytest-7.0.0, pluggy-1.0.0
rootdir: /Users/hoge/src
collected 13 items                                                                                                                                                    

test_hogehoge.py .............                                                                                                                        [100%]

========================================================================= 13 passed in 20.66s =========================================================================

test 実装時の参考

5
2
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
5
2