ファイルの読み書きをするプログラムをテストするとき、実際にファイルを用意するのは面倒。
ファイルの読み書きは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が作られたのかな、、?