python×Cloud Functions×FirestoreでAPIを簡単に作ってみる Pt2 テスト編(詳細)
概要
Firestoreのデータ操作を行う関数をローカルでテストするためのあれこれをまとめます。やり方としては、Flaskのrequestオブジェクトを使ってローカルにAPIを作成します。
- 前回の記事はこちら
開発環境
- 開発環境
- MacOS
- python3.7
- pycharm
新規プロジェクトの作成
pycharmまたはpyvenvコマンドを用いて新規プロジェクトを作成します。画面イメージはpycharmですが、デフォルトで作成されるフォルダに加えて「.auth」「functions」も作成しています。また、ファイルは省略します。
関数の作成
テストする関数はPt1と同じです。functions/に置きます。
import json
from google.cloud import firestore
# ソースはPt1と同じ
def get_filtered_user(request):
request_json = request.get_json()
if request.args and 'name' in request.args:
request_name = request.args.get('name')
elif request_json and 'name' in request_json:
request_name = request_json['name']
else:
request_name = ''
db = firestore.Client()
query = db.collection('user').where('name', '==', request_name)
docs = query.get()
users_list = []
for doc in docs:
users_list.append(doc.to_dict())
return_json = json.dumps({"users": users_list}, ensure_ascii=False)
return return_json
ローカルにAPIを作る
Cloud FunctionsではFlaskのrequestオブジェクトを使って、リクエスト条件が関数に受け渡しされます。なので、ローカルにFlaskを使ってAPIを作ることで、テスト可能になります。
以下のようなソースを作ります。
from flask import Flask, request, abort, render_template, send_from_directory
# 作成した関数を外部ソースからimport
from functions.getFilteredUser import get_filtered_user
app = Flask(__name__)
# methodにPOST等も明示的に指定(FlaskではデフォルトGETのみのため)
# '/~'がテストするときのパス
@app.route('/get-filtered-user', methods=['GET', 'POST'])
def local_api():
return get_filtered_user(request)
if __name__ == '__main__':
app.run()
これでhttp://localhost:5000/get-filtered-user
にリクエストを投げると、get_filtered_user
が実行されるようになります。
但し、関数がローカルからFirebaseにアクセスするには、秘密鍵を環境変数に読み込ませる必要があります。
秘密鍵の取得・設定
秘密鍵の取得方法は以下のいずれかのページがわかりやすいです。
Cloud FirestoreのデータをPythonで取得する
Firebase公式 - SDK の初期化
取得したファイルのパスを、GOOGLE_APPLICATION_CREDENTIALS
の環境変数に設定することでローカルからのFirestoreへのアクセスが可能になります。環境変数の設定は以下のような方法があります。
[方法1] pycharmの「構成の編集>環境変数」から追加
pycharmではこのやり方が使えます。ソースごとに環境変数が設定可能なので、複数のFirebaseプロジェクトを扱う場合でも、bash_profileを書き換える必要がなくなります。
この例では「.auth/firebase_auth.json」という秘密鍵ファイルを読み込ませています。
[方法2] ターミナルから環境変数を設定
$ export GOOGLE_APPLICATION_CREDENTIALS="<保存先>/<秘密鍵ファイル>.json"
# .bash_profileに追記する場合 (ターミナルの再起動が必要)
$ echo 'export GOOGLE_APPLICATION_CREDENTIALS="<保存先>/<秘密鍵ファイル>.json"' >> ~/.bash_profile
作成したソースをターミナルからpython sample.py
と実行する場合、このやり方が一番シンプルです。
[方法3] pythonのソースにベタ書き
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "<保存先>/<秘密鍵ファイル>.json"
ソースの中に書きたい事情がある場合はこれでもいけると思います。
ディレクトリの最終状態
functionsと.authを含めて以下の状態になっているはず。functions/getUser.py
はPt1のソースが残ってるだけなので無くても問題ありません。
ローカルでのテスト
環境変数を設定した状態で、localServer.pyを実行してhttp://localhost:5000/get-filtered-user
にリクエストを投げます。
$ python localServer.py
* Serving Flask app "localServer" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [04/Aug/2019 14:51:35] "POST /get-filtered-user HTTP/1.1" 200 -
$ curl -X POST -H "Content-Type: application/json" -d '{"name": "Alice"}' http://localhost:5000/get-filtered-user
{"users": [{"age": 19, "name": "Alice", "gender": "female"}]}
これで、Cloud Functionsにデプロイせずともテストできるようになりました。デプロイすると2分ほど待ちますが、この方法だとすぐにテストできます。localServer.pyのコンソールにはprintの内容が出力されるのでデバッグも容易になります。
また、pycharmの場合、pythonの実行とターミナルを並べて実行すれば便利です。
もちろんPostmanを使ってもOKです。
おわりに
Cloud Functionsではいきなりデプロイするのではなく、以下のような流れでコードを作成すると、どこでバグってるかがわかりやすいので、ハマることが少なく開発できると思います。
- デプロイしたい関数だけでテスト
- ローカルにAPIを作成してテスト
- Cloud Functionsにデプロイ
1では、リクエスト条件などをベタ書きで書いてみて、関数が想定通り動くか確認します。2では、GETやPOSTでリクエストが渡された時の動きを確認できます。
今回は、この中の2のやり方を書きました。
Pt3では、3のデプロイをローカルから行う方法を書こうと思います。
参考リンク
How to develop Python Google Cloud Functions
ローカルでの関数の実行
MacでCloud Machine Learning Engineを利用してみる
Author And Source
この問題について(python×Cloud Functions×FirestoreでAPIを簡単に作ってみる Pt2 テスト編(詳細)), 我々は、より多くの情報をここで見つけました https://qiita.com/ny7760/items/01dacd4df434f4a5be25著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .