2013-06-13 9 views
7

私はparser = ParserClass('/path/to/file')のようなファイル名を与えてインスタンス化するクラスを持っています。そして、私はparser.parse()というメソッドを呼び出してファイルを読み込みます。
今、私はユニットテストしたい悪い何かが内部で起こった場合、その:正しい例外が発生しますので、私はこのような__builtin__.openをモックとしたいPythonで擬似ファイルオブジェクトを使って単体テストを行う方法は?

with open(filename, 'rb') as fp: 
    // do something 

from mock import MagicMock, patch 
from StringIO import StringIO 

test_lines = StringIO("""some test lines, emulating a real file content""") 
mock_open = MagicMock(return_value=test_lines) 
with patch('__builtin__.open', mock_open): 
    self.mock.parse() 

が、これは私を与えますAttributeError: StringIO instance has no attribute '__exit__'
StringIOはファイルオブジェクトとまったく同じように動作しますが、そうではありません。

モックオブジェクトで特定のコンテンツ(test_lines)を使ってこのメソッドをテストするにはどうすればよいですか?代わりに私は何を使うべきですか?

答えて

9

あなたはコンテキストマネージャ提供するStringIOをサブクラス化することができます:

class ContextualStringIO(StringIO): 
    def __enter__(self): 
     return self 
    def __exit__(self, *args): 
     self.close() # icecrime does it, so I guess I should, too 
     return False # Indicate that we haven't handled the exception, if received 


test_lines = ContextualStringIO(...) 

総投機:StringIOオブジェクトがドロップイン代替品fileオブジェクトため除くコンテキストマネージャの不足のためであれば、Iこれがうまくいくかどうか疑問:何Fiの

class ContextualStringIO(StringIO, file): 
    pass 

ContextualStringIO継承le操作はStringIOから可能ですが、それ以外はすべてfileから継承されています。それはエレガントに見えますが、おそらく広範なテスト(またはこれがうまくいかない理由を説明するためにPythonの内部構造に詳しい方)が必要です。

+0

'TypeError:__exit __()はちょうど1つの引数(4与えられます)' – kissgyorgy

+0

@ウォークマン:ありがとう。私は '__exit__'で受け取った例外を処理しないので、任意の数の引数を取るように更新しました。適切な実装が必要です。私はコンテキスト・マネージャーには、オープン・メソッドが、例外を処理するかどうかを知るために、あるいは単にファイルをクローズし、例外を再発生させることができるかどうかを知るために十分に精通していません。 – chepner

+1

[ドキュメントから:](http://docs.python.org/2/reference/compound_stmts.html#with) スイートが例外のために終了し、__exit __()メソッドの戻り値がfalseの場合、例外はリレイズされます。戻り値がtrueの場合、例外は抑制され、with文の後の文で実行が継続されます。 – kissgyorgy

6

これは、StringIOがコンテキストマネージャプロトコルを実装していないという既知の問題です。

common recipeは、以下である:

from contextlib import contextmanager 


@contextmanager 
def StringIO(): 
    """Add support for 'with' statement to StringIO - http://bugs.python.org/issue1286 
    """ 
    try: 
     from cStringIO import StringIO 
    except ImportError: 
     from StringIO import StringIO 

    sio = StringIO() 

    try: 
     yield sio 
    finally: 
     sio.close() 

それはStringIOのコンテキストマネージャプロトコルを実装し、それがwith文で使用されることを可能にします。


UPDATEまあ、私は直接文字列から読み取ることができるmock_openの存在を発見したので、それはおそらく行く方法です。 (注言うように)これだけunicodeタイプで使用することができますしかし

The io module provides the Python interfaces to stream handling. Under Python 2.x, this is proposed as an alternative to the built-in file object, but in Python 3.x it is the default interface to access files and streams.

+0

ありがとう!これは動作しますが、 'StringIO(data)'と 'sio = StringIO(data)'を追加して、それをパラメータで構築できるようにしてください。 – kissgyorgy

+0

もう一度強調すると、アップデートは重要です。 'mock_open'はこの目的のためのもので、http://bugs.python.org/issue21258にもかかわらず理想的な解決策であるはずです。 – 0xc0de

0

ありa provisionmockライブラリに特にこの目的のために、である:それは(あなたがすぐfor line in opened_mock_fileを行うことができないので、すなわち__iter__方法。)デフォルトのイテレータをサポートして行方不明にissueを持ってい

が、記載された通りに処理することができる。here

私は@ icecrimeの回答をupvotedしていますが、彼の更新部分は十分に強調表示されていないようです。

関連する問題