継続は力なり

タイトル通り定期的な更新を心掛けるブログです。

pytest と moto でモックを作ったテストを行う

タダです.

以前から気になっていた moto というツールを試す機会があったので,この記事で導入と pytest と組み合わせたテストをチュートリアルの内容を通して見ていきたいと思います.

moto って?

moto とはテストで AWS サービスをモックで作っていくための Python ライブラリです.

github.com

導入はpipでさくっとできます.なお,motoは全ての AWS サービスを呼び出す関数を網羅しているわけではなさそうなので,その点は注意です.

$ pip install moto

moto が対応しているサービス一覧 github.com

moto でモックを作り pytest でテストする

それでは, moto を使ってみようと思います.

$ pipenv install boto3
$ pipenv install --dev pytest moto

以下のようなディレクトリ切って,app.pyとそのテストをするためのtest.pyを作りました.

.
├── app
│   ├── app.py
│   └── __init__.py
└── tests
    ├── __init__.py
    └── test.py

チュートリアルとして S3 に put_object を実行する save 関数があるコードを用意しました.

import boto3

class MyModel(object):
    def __init__(self, name, value):
        self.name = name
        self.value = value

    def save(self):
        s3 = boto3.client('s3', region_name='ap-northeast-1')
        s3.put_object(Bucket='mybucket', Key=self.name, Body=self.value)

テスト用コードとしては次のものを用意しました.@mock_s3を記述することで S3 へのアクセスをモックしてくれてます.save関数を呼んで,オブジェクトを読みファイルの中身がis awesomeかを確認するようになっています.

import boto3
from moto import mock_s3
from app.app import MyModel

@mock_s3
def test_my_model_save():
    conn = boto3.resource('s3', region_name='ap-northeast-1')
    conn.create_bucket(Bucket='mybucket',CreateBucketConfiguration={
        'LocationConstraint':'ap-northeast-1'
    })

    model_instance = MyModel('steve', 'is awesome')
    model_instance.save()

    body = conn.Object('mybucket', 'steve').get()[
        'Body'].read().decode("utf-8")

    assert body == 'is awesome'

テストコードを走らせてみたところテストをパスしたのを確認できました.

$ pipenv run pytest tests/test.py 
=============================================================================== test session starts ===============================================================================
platform linux -- Python 3.7.9, pytest-6.2.3, py-1.10.0, pluggy-0.13.1
rootdir: /xxxx/xxxx/environment
collected 1 item                                                                                                                                                                  

tests/test.py .                                                                                                                                                             [100%]

================================================================================ warnings summary =================================================================================
../.local/share/virtualenvs/environment-QZ1wNgYc/lib64/python3.7/distutils/__init__.py:1
  /xxxx/xxxx/.local/share/virtualenvs/environment-QZ1wNgYc/lib64/python3.7/distutils/__init__.py:1: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
    import imp

-- Docs: https://docs.pytest.org/en/stable/warnings.html
========================================================================== 1 passed, 1 warning in 0.69s ===========================================================================

まとめ

簡単になりますが,motoを使ったテストコードを書くことをやってみました.AWS リソースを作ってまで確認するとコストがかかって確認が遅くなってしまうような状況ならこう言ったツールを作って素早く確認して行けると良さそうです.