AWS Chalice とは
AWS SDK for Python を開発するチームによってメンテナンスされている、AWS Lambda に展開するアプリケーションを実装するための Python のマイクロフレームワークです。
ビルトインされている @api.route
, @app.on_s3_event
などのデコレータを用いて関数を実装し、CLI で簡単にそれらを実際に AWS 上に展開することが出来るようになります。
例えば、chalice CLI の中にある chalice new-project
コマンドでプロジェクトの雛形を作成するとこのような実装が生成されます。
from chalice import Chalice
app = Chalice(app_name='test')
@app.route('/')
def index():
return {'hello': 'world'}
/
にアクセスしたら {"hello": "world"}
が返ってきそうだということがひと目でわかりますよね。
では実際にこれを AWS 上で展開するためには、ですが、以下のコマンドを実行します。
$ chalice deploy
このコマンドが成功すると、AWS 上で Amazon API Gateway と連携する AWS Lambda が関連設定も自動で行われた状態でデプロイされます。つまり、この瞬間からこのエンドポイントにアクセスすることが出来るということです。
より詳細、より実践的な使用方法は公式ドキュメントや他のエントリに解説を譲りますが、実装完了から最短で数十秒でデプロイすることが適います。
AWS Chalice で実装したアプリケーションのユニットテスト
さてここからが本題です。AWS Chalice で実装したアプリケーションのユニットテストはどのように書くことが出来るでしょうか? Chalice のバージョン 1.12.0
現在では、ビルトインでのテストスイートの実装は含まれていません。
このトピックは多くの Chalice ユーザーの関心事であり、これまでにもいくつかの議論が発生しています。
たとえばこのスレッドでは、Chalice に備わっているローカル起動のための実装をユニットテストに転用したらいいのではないかという意見がマジョリティとなっていました。
https://github.com/aws/chalice/issues/289
そして、このアプローチを pytest
で利用出来るようにした pytest-chalice
という実装があります。
https://pypi.org/project/pytest-chalice/
pytest-chalice の使い方
ここでは pytest
そのものについての解説は割愛します。必要に応じて別の場所でインプットしてください。
では実際にシンプルな API のための実装のユニットテストを記述してみましょう。まずは pytest-chalice
をインストールしましょう。
$ pip install pytest-chalice
そして先述の例が再登場です。/
にアクセスしたら {"hello": "world"}
が返ってきそうだということがひと目でわかりますよね (2回目)。
# project/app.py
from chalice import Chalice
app = Chalice(app_name='project')
@app.route('/')
def index():
return {'hello': 'world'}
このアプリケーションのプロジェクトルートは project/
としています。続いては同じディレクトリ配下に以下のレイアウトで tests
ディレクトリと各ファイルを設置します。
$ tree -C tests
tests/
├── __init__.py
├── conftest.py
└── test_app.py
0 directories, 3 files
まずは conftest.py
を以下のように実装します。app
という名前で、実際にテストを行いたい対象の Chalice アプリケーションオブジェクトを返す fixture を設けることが非常に重要なポイントです。pytest-chalice
はこの fixture から返却されるオブジェクトを用いて内部的にローカル起動し、そこにアクセスするためのテストクライアントオブジェクトを新たに fixture として登録しています。
# project/tests/conftest.py
import pytest
from app import app as chalice_app
@pytest.fixture
def app():
return chalice_app
続いて実際に app.py
に対してのテストを実装してみましょう。/
にアクセスしたら {"hello": "world"}
が返って来そうな実装(3回目)なので、以下の挙動を確認する内容で記述してみます。
-
/
にアクセスしたら{"hello": "world"}
が返ってくる - レスポンスステータスが
200 OK
# project/tests/test_app.py
from http import HTTPStatus
def test_index(client):
response = client.get('/')
assert response.status_code == HTTPStatus.OK
assert response.json == {'hello': 'world'}
test_index
で受け取っている client
が、先述した pytest-chalice
が生成しているテストクライアントオブジェクトです。app
fixture を設けていないと正常に動作しません。
さてここまででシンプルなアプリケーションに対してシンプルなユニットテストの実装が完了しました。実行してみましょう。
===================== test session starts =====================
platform darwin -- Python 3.7.4, pytest-5.2.2, py-1.8.0, pluggy-0.13.0
cachedir: .pytest_cache
rootdir: /private/tmp/project
plugins: chalice-0.0.4, env-0.6.2
collected 1 item
tests/test_app.py::test_index PASSED [100%]
====================== 1 passed in 0.01s ======================
以上です。類似の Example はこちらにありますのでスッと試したいという場合はご利用ください。
https://github.com/studio3104/pytest-chalice/tree/master/examples/simple
pytest-chalice における注意点 (2019/12/08 現在)
- 3rd party project である
- Beta Stage である
ということに注意してください。現在コア機能を Chalice 本体に取り込むための PR が進行していますので、どのように取り込まれるかによっては将来のリリースでインターフェースなどが変更になる可能性があります。使い方がガラッと変わってしまうほどでにはならないはずですが、たとえば真面目に Type Hint を書いている場合はクラス名など名前空間が変わることで影響を受けるかもしれません。
-
@app.route
以外の関数のテストを書きたい場合
現状では @app.on_s3_event
, @app.on_sqs_message
などで修飾されている関数のための特別な実装は用意がありません。もしこれらのテストを行いたい場合は、これらの event context を表現する fixture を用意して、それを用いるようにテストを実装するアプローチになると思います。