記事について
pythonでS3をモック化する際に、以前はpython標準のMockを使っていたけど、boto3に標準のStubberという便利なモジュールが用意されているので、使用してみた。
なお、コードは、実際に動かしたものを公開用に修正したもので、動作確認はしておりません。ご了承ください。
環境
- python3系
- boto3
テストコードを書いてみる
S3からオブジェクトをGETする
対象コード
import boto3
func_get():
s3 = boto3.resource('s3')
bucket = 'bucket'
key = '/key/object.json'
obj = s3.Object(bucket, key)
res = obj.get()
return res
テストコード
正常系
from botocore.stub import Stubber
from botocore.exceptions import ClientError
from unittest import TestCase
from unittest import mock
import boto3
import contextlib
class TestGetGehirnFiles(TestCase):
def test_get_files_OK(self):
s3 = boto3.resource('s3')
stubber = Stubber(s3.meta.client)
# Bodyの中:botocore.response.StreamingBody = ファイルストリーム
body = open('./test_body.json')
res = {"ResponseMetadata": {"HTTPStatusCode": 200}, 'Body': body}
stubber.add_response('get_object', res)
stubber.activate()
# ここではcontextlibで書いてるけど、Mock対象が一つの場合にはcontextlibを使用する必要はない。
with contextlib.ExitStack() as stack:
stack.enter_context(mock.patch('boto3.resource', return_value=s3))
ex_body = open('./test_body.json')
expectation = {"ResponseMetadata": {"HTTPStatusCode": 200}, 'Body': ex_body}
res = func_get()
self.assertDictEqual(expectation, res)
body.close()
ex_body.close()
異常系
例外を返す
- body = open('./test_body.json')
- res = {"ResponseMetadata": {"HTTPStatusCode": 200}, 'Body': body}
- stubber.add_response('get_object', res)
+ stubber.add_client_error('get_object', service_error_code='AccessDenied')
複数回の呼び出しのレスポンスを定義する場合
ファーストインファーストアウトで定義できるので、例えば、例外を2回返した後に正常レスポンスを返す場合、以下のように記載する
s3 = boto3.resource('s3')
stubber = Stubber(s3.meta.client)
stubber.add_client_error('get_object', service_error_code='AccessDenied')
stubber.add_client_error('get_object', service_error_code='AccessDenied')
stubber.add_response('get_object', xxx)
stubber.activate()
S3にオブジェクトをPUTする
基本的にGetと同じなので、省略。以下だけ変更すれば良い
- stubber.add_response('get_object', res)
+ stubber.add_response('put_object', res)