雑念ストレージ

プログラミング関連のメモとか

【Python】mock_openでファイルの読み書きをMock化する

ファイルの読み書きをするプログラムをテストするとき、実際にファイルを用意するのは面倒。
ファイルの読み書きはMock化したいが、そんなときはPythonが公式で用意してくれているmock_open関数が便利。

たとえば、以下のようなプログラムをテストしたいとする。
(テキストファイルを読み込んで、1行追加した内容で別のファイルに保存しているだけ)

def func(input_file_path: str, output_file_path: str):

    with open(input_file_path, 'r', encoding='UTF-8') as f:
        data = f.read()

    with open(output_file_path, 'w', encoding='UTF-8') as f:
        f.write(data + '\nAPPEND!')

ファイルの読み書きをMock化してテストする場合、以下のようになる。

import unittest
from unittest.mock import mock_open, patch, call

import read_write  # テスト対象のスクリプト


class TestTextAppender(unittest.TestCase):

    def test_append(self):
        m = mock_open(read_data='INITIAL TEXT')
        with patch("builtins.open", m):
            # テスト対象のメソッドを実行
            read_write.func("input.txt", "output.txt")

        # ファイル読み込みのためのopenが呼ばれたことの確認
        self.assertEqual(m.call_args_list[0], call('input.txt', 'r', encoding='UTF-8'))
        # ファイル書き込みのためのopenが呼ばれたことの確認
        self.assertEqual(m.call_args_list[1], call('output.txt', 'w', encoding='UTF-8'))

        # mock化されたopenを呼び、ファイルオブジェクト(これもmock化されている)を取得する
        file = m()
        # ファイル書き込みのwriteが呼ばれたことの確認
        file.write.assert_called_once_with("INITIAL TEXT\nAPPEND!")

入力ファイルはmock_openメソッドの引数read_dataに設定すればOK。
出力ファイルはファイルオブジェクトのwriteから取得する。

mock_open についてのメモ

公式のリファレンスを見ると、mock_openは以下のように説明されている。

open() の利用を置き換えるための mock を作るヘルパー関数。 open() を直接呼んだりコンテキストマネージャーとして利用する場合に使うことができます。

コンテキストマネージャーとは、withブロックで使うことでファイルやリソースの開放を自動でやってくれるやつのこと。
JavaでいうClosableやAutoClosableみたいなもの。

ファイルの入出力は、コンテキストマネージャーとしての処理とreadやwriteの処理があって、mock化するのが大変だからmock_openが作られたのかな、、?