LoginSignup
0
0

mock関数をclassにまとめてpytest.fixtureを使う

Last updated at Posted at 2024-06-19

mock関数をclassでまとめたいときにpytest.fixture周りで詰まった点に関しての記事です。

ここでは、S3とAPI callの例で説明します。

# サンプルコード
class S3Client:
    def __init__(self, bucket: str):
        session = boto3.Session()
        self._s3_client = session.client("s3")

    def sample_func(self, s3_object_key: str) -> bool:
        return "output"

def api_call():
    return "output"


class TestClass:
    def __init__(self, s3_client):
        self._s3_client = s3_client

    def run(self, api_call):
        return api_call.call()

mock関数に書く方法

以前は、mock関数以下のように作成していました。

# mock関数
@pytest.fixture
def mocker_s3_client(mocker):
    return mocker.Mock(spec=S3Client)


@pytest.fixture
def mocker_api_call(mocker):
    return mocker.Mock(call=mocker.Mock(return_value=None))


# テストコード
def test_A(mocker_s3_client, mocker_api_call):
    actual = TestClass(s3_client=mocker_s3_client).run(mocker_api_call)
    assert actual == "test"

この場合、

  • mockする関数が増えた場合に引数が増える
  • テストファイルを分けるときに、importが増える

ため、なるべくclassとしてまとめたいと思いました。

mock関数をclassにまとめる方法

まず、mock関数をMockClassにまとめます。

# mockクラス
class MockClass:
    def __init__(self, mocker):
        self._mocker = mocker

    def mocker_s3_client(self):
        return self._mocker.Mock(spec=S3Client)

    def mocker_api_call(self):
        return self._mocker.Mock(call=self._mocker.Mock(return_value=None))

この状態で、pytest.fixutreをclassか関数の上にデコレートしようとしましたが、動きませんでした。

うまくいったやり方としては、MockClassを呼び出す関数を作ることです。

@pytest.fixture
def create_mock_class(mocker):
    return MockClass(mocker=mocker)

テストコード側はcreate_mock_classを呼び出します。

def test_A(create_mock_class: MockClass):
    mock_s3_client = create_mock_class.mocker_s3_client()
    mock_api_call = create_mock_class.mocker_api_call()

    mock_s3_client.sample_func.return_value = True

    actual = TestClass(s3_client=mocker_s3_client).run(mocker_api_call)
    assert actual == "test"

def test_B(create_mock_class: MockClass):
    mock_s3_client = create_mock_class.mocker_s3_client()
    mock_api_call = create_mock_class.mocker_api_call()

    mock_s3_client.sample_func.return_value = None

    actual = TestClass(s3_client=mocker_s3_client).run(mocker_api_call)
    assert actual == "test"
0
0
0

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
  3. You can use dark theme
What you can do with signing up
0
0