こんにちは。サービス開発室の武田です。
私が開発しているプロダクトではPythonのユニットテストにmotoを使用しています。motoはPythonのAWS SDKであるboto3のモックライブラリです。ユニットテストを実行するにあたって、実際にAWS上にリソースを作成してしまうと料金もかかりますし、ネットワーク起因など直接関係ないところでエラーが発生してしまう可能性があります。motoを使用することでそういった環境依存を排除してユニットテストを実施できます。
これまでmotoのやや古いバージョンを使用していたのですが、一気に最新であるv5.0.1まで上げることにしました(執筆時点での最新は5.0.2)。その際に、これまで通っていたIAM関係のテストが失敗するようになりました。具体的には次のエラーメッセージが出ました。
botocore.errorfactory.NoSuchEntityException: An error occurred (NoSuchEntity) when calling the AttachRolePolicy operation: Policy arn:aws:iam::aws:policy/ReadOnlyAccess does not exist or is not attachable.
このエラーメッセージは、IAMの管理ポリシー(ここではReadOnlyAccess)が見つからないといっています。
原因はAWS管理ポリシーがデフォルトではロードされなくなったためです。次の2つのいずれかを実施すれば解決します。
- デコレーターにロードを指示する
@mock_aws(config={"iam": {"load_aws_managed_policies": True}})
- 環境変数でロードを指示する
MOTO_IAM_LOAD_MANAGED_POLICIES=true
確認してみる
実行環境は次のようになっています。
- Python: 3.12
- boto3: 1.34.51
- pytest: 8.0.2
- moto: 5.0.2
動作確認はシンプルなコードで確認できます。まずはpoetryを使ったプロジェクトを作成します(pipenvでもなんでもいいです)。
$ mkdir work && cd $_
$ poetry init -n
$ poetry add boto3 pytest moto
実行するテストコードを用意します。
test_main.py
import boto3
from moto import mock_aws
@mock_aws
def test_main():
ROLE_NAME = "test-role"
iam = boto3.client("iam")
iam.create_role(RoleName=ROLE_NAME, AssumeRolePolicyDocument="{}")
<pre><code>iam.attach_role_policy(
RoleName=ROLE_NAME,
PolicyArn="arn:aws:iam::aws:policy/ReadOnlyAccess",
)
</code></pre>
作成したロールに対してattach_role_policy
メソッドでarn:aws:iam::aws:policy/ReadOnlyAccess
をアタッチしようとしています。これで一度テストを実行してみます。
$ poetry run pytest test_main.py
...
error_class = self.exceptions.from_code(error_code)
<blockquote>
<pre><code> raise error_class(parsed_response, operation_name)
</code></pre>
E botocore.errorfactory.NoSuchEntityException: An error occurred (NoSuchEntity) when calling the AttachRolePolicy operation: Policy arn:aws:iam::aws:policy/ReadOnlyAccess does not exist or is not attachable.
</blockquote>
.venv/lib/python3.12/site-packages/botocore/client.py:1009: NoSuchEntityException
冒頭でも紹介したエラーが出ました。ではこれを修正しましょう。
test_main.py
import boto3
from moto import mock_aws
@mock_aws(config={"iam": {"load_aws_managed_policies": True}})
def test_main():
ROLE_NAME = "test-role"
iam = boto3.client("iam")
iam.create_role(RoleName=ROLE_NAME, AssumeRolePolicyDocument="{}")
<pre><code>iam.attach_role_policy(
RoleName=ROLE_NAME,
PolicyArn="arn:aws:iam::aws:policy/ReadOnlyAccess",
)
</code></pre>
変わったのは@mock_aws
の部分です。これで再度テストを実行してみます。
$ poetry run pytest test_main.py
collected 1 item
test_main.py .
今度は成功しました!
まとめ
最初からきちんとCHANGELOGを読んでおけばよかったのですが、見落としていたため修正するのに時間がかかってしまいました。motoはv5からデコレーションがシンプルになった(@mock_aws
付けるだけ)ためコードもすっきりします。ぜひ試してみてください。