LoginSignup
3

More than 3 years have passed since last update.

posted at

updated at

AWS Chalice で実装したアプリケーションのユニットテスト

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 を用意して、それを用いるようにテストを実装するアプローチになると思います。

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
What you can do with signing up
3