ファイル操作をmock化する
open とか codecs.open でファイルを開く場合は基本 with句で使っているので単純にopenをモックに書き換えようとすると相当面倒くさい。そのために mock_open という関数が用意されている。
open() をコンテキストマネージャーとして使う方法は、ファイルが必ず適切に閉じられるようにする素晴らしい方法で、今では一般的になっています
問題は、 open() をモックアウトしたところで、コンテキストマネージャーが使われる (__enter__() と __exit__() が呼ばれる) のはその 戻り値 だということです。
コンテキストマネージャーを MagicMock でモックするのは一般的かつ面倒なので、ヘルパー関数を用意しています。https://docs.python.jp/3/library/unittest.mock.html#mock-open
具体的な使い方は以下の通り。 Read/Writeともに同じ open 関数を使っている限りは両方の呼び出しがmockされる。
#!/usr/bin/python # -*- coding: utf-8 -*- import codecs from unittest.mock import MagicMock, patch, mock_open # python 2 の場合は from mock from nose.tools import eq_, ok_ def readwrite(): with codecs.open('read.txt', 'r') as fh: reads = fh.readlines() with codecs.open('write.txt', 'w') as fh: fh.write('line1\n') fh.write('line2\n') fh.write('line3\n') return reads def test_mock_open(): mockio = mock_open(read_data='readline1\nreadline2\nreadline3\n') with patch('codecs.open', mockio): reads = readwrite() # 1. read mocked data eq_(reads, ['readline1\n', 'readline2\n', 'readline3\n']) # 2. how to check write contents fh = mockio() # Get file handler mock object by calling mockio object actuals = [args[0] for (args, kwargs) in fh.write.call_args_list] eq_('line1\n', actuals[0]) eq_('line2\n', actuals[1]) eq_('line3\n', actuals[2]) if __name__ == '__main__': test_mock_open()