253

unittest.mock次のコードを次のようにテストするにはどうすればよいですか?

def testme(filepath):
    with open(filepath) as f:
        return f.read()
4

10 に答える 10

355

Python 3

フレームワークの一部であるパッチbuiltins.openと使用。コンテキストマネージャーとして使用すると、パッチが適用されたオブジェクトを置き換えるために使用されるオブジェクトが返されます。mock_openmockpatch

from unittest.mock import patch, mock_open
with patch("builtins.open", mock_open(read_data="data")) as mock_file:
    assert open("path/to/open").read() == "data"
mock_file.assert_called_with("path/to/open")

patchデコレータとして使用する場合は、mock_open()の結果をnew=引数として使用patchすると、少し奇妙になる可能性があります。代わりに、patch's引数を使用し、ドキュメントで説明されているように、使用しないnew_callable=余分な引数はすべて関数に渡されることに注意してください。patchnew_callablepatch

patch()任意のキーワード引数を取ります。これらは、構築時にMock(またはnew_callable)に渡されます。

@patch("builtins.open", new_callable=mock_open, read_data="data")
def test_patch(mock_file):
    assert open("path/to/open").read() == "data"
    mock_file.assert_called_with("path/to/open")

この場合patch、モックオブジェクトを引数としてテスト関数に渡すことに注意してください。

Python 2

__builtin__.openの代わりにパッチを適用する必要があり、その一部ではありませんbuiltins.open。個別にパッチを適用してインポートする必要があります。mockunittestpip install

from mock import patch, mock_open
with patch("__builtin__.open", mock_open(read_data="data")) as mock_file:
    assert open("path/to/open").read() == "data"
mock_file.assert_called_with("path/to/open")
于 2016-01-08T13:00:13.397 に答える
149

これを行う方法は、特にMagicMockを使用してPythonプロトコルメソッド(マジックメソッド)のモックを最終的にサポートするモック0.7.0で変更されました。

http://www.voidspace.org.uk/python/mock/magicmock.html

コンテキストマネージャーとして開くモックの例(モックドキュメントの例のページから):

>>> open_name = '%s.open' % __name__
>>> with patch(open_name, create=True) as mock_open:
...     mock_open.return_value = MagicMock(spec=file)
...
...     with open('/some/path', 'w') as f:
...         f.write('something')
...
<mock.Mock object at 0x...>
>>> file_handle = mock_open.return_value.__enter__.return_value
>>> file_handle.write.assert_called_with('something')
于 2011-05-24T14:56:44.240 に答える
80

最新バージョンのmockでは、非常に便利なmock_openヘルパーを使用できます。

mock_open(mock = None、read_data = None)

openの使用を置き換えるためのモックを作成するヘルパー関数。直接呼び出されたオープンまたはコンテキストマネージャーとして使用されたオープンで機能します。

モック引数は、設定するモックオブジェクトです。None(デフォルト)の場合、MagicMockが作成され、APIは標準のファイルハンドルで使用可能なメソッドまたは属性に制限されます。

read_dataは、返すファイルハンドルのreadメソッドの文字列です。これはデフォルトでは空の文字列です。

>>> from mock import mock_open, patch
>>> m = mock_open()
>>> with patch('{}.open'.format(__name__), m, create=True):
...    with open('foo', 'w') as h:
...        h.write('some stuff')

>>> m.assert_called_once_with('foo', 'w')
>>> handle = m()
>>> handle.write.assert_called_once_with('some stuff')
于 2013-10-02T20:28:51.807 に答える
15

単純なファイルにmock_openを使用するには(このページですでに提供されてread()いる元のmock_openスニペットは、書き込み用に調整されています):

my_text = "some text to return when read() is called on the file object"
mocked_open_function = mock.mock_open(read_data=my_text)

with mock.patch("__builtin__.open", mocked_open_function):
    with open("any_string") as f:
        print f.read()

mock_openのドキュメントによると、これは特にのためのものであるため、たとえば、のread()ような一般的なパターンでは機能しません。for line in f

python 2.6.6 /mock1.0.1を使用します

于 2014-12-11T18:19:22.680 に答える
5

一番上の答えは役に立ちますが、少し拡張しました。

渡された引数に基づいてファイルオブジェクト( fin )の値を設定する場合は、次の1つの方法があります。as fopen()

def save_arg_return_data(*args, **kwargs):
    mm = MagicMock(spec=file)
    mm.__enter__.return_value = do_something_with_data(*args, **kwargs)
    return mm
m = MagicMock()
m.side_effect = save_arg_return_array_of_data

# if your open() call is in the file mymodule.animals 
# use mymodule.animals as name_of_called_file
open_name = '%s.open' % name_of_called_file

with patch(open_name, m, create=True):
    #do testing here

基本的にopen()、オブジェクトを返し、そのオブジェクトwithを呼び出し__enter__()ます。

適切にモックopen()するには、モックオブジェクトを返すようにモックする必要があります。次に、そのモックオブジェクトは、その__enter__()呼び出しをモックして(MagicMockこれを実行します)、必要なモックデータ/ファイルオブジェクトを返します(したがってmm.__enter__.return_value)。上記の方法で2つのモックを使用してこれを行うと、渡された引数をキャプチャしopen()てメソッドに渡すことができますdo_something_with_data

モックファイル全体を文字列としてに渡しましたが、次のようになりましたopen()do_something_with_data

def do_something_with_data(*args, **kwargs):
    return args[0].split("\n")

これにより、文字列がリストに変換されるため、通常のファイルの場合と同じように次のことができます。

for line in file:
    #do action
于 2016-06-27T20:45:53.317 に答える
3

ゲームに少し遅れるかもしれませんがopen、新しいファイルを作成せずに別のモジュールを呼び出すと、これでうまくいきました。

test.py

import unittest
from mock import Mock, patch, mock_open
from MyObj import MyObj

class TestObj(unittest.TestCase):
    open_ = mock_open()
    with patch.object(__builtin__, "open", open_):
        ref = MyObj()
        ref.save("myfile.txt")
    assert open_.call_args_list == [call("myfile.txt", "wb")]

MyObj.py

class MyObj(object):
    def save(self, filename):
        with open(filename, "wb") as f:
            f.write("sample text")

モジュールopen内の関数を自分にパッチすることで、ファイルを作成せずにファイルへの書き込みをモックすることができます。__builtin__mock_open()

注:cythonを使用するモジュールを使用している場合、またはプログラムがcythonに何らかの形で依存している場合は、ファイルの先頭に含めることによってcythonの__builtin__モジュールをインポートする必要がありimport __builtin__ます。__builtin__Cythonを使用している場合、ユニバーサルをモックすることはできません。

于 2015-09-24T18:35:29.003 に答える
2

これ以上ファイルが必要ない場合は、テストメソッドを装飾できます。

@patch('builtins.open', mock_open(read_data="data"))
def test_testme():
    result = testeme()
    assert result == "data"
于 2020-05-14T00:12:30.460 に答える
0

組み込みのopen()関数にunittestでパッチを適用するには:

これは、json構成を読み取るパッチに対して機能しました。

class ObjectUnderTest:
    def __init__(self, filename: str):
        with open(filename, 'r') as f:
            dict_content = json.load(f)

モックオブジェクトは、open()関数によって返されるio.TextIOWrapperオブジェクトです。

@patch("<src.where.object.is.used>.open",
        return_value=io.TextIOWrapper(io.BufferedReader(io.BytesIO(b'{"test_key": "test_value"}'))))
    def test_object_function_under_test(self, mocker):
于 2020-03-11T11:41:16.753 に答える
-1

githubスニペットから供給され、Pythonの読み取りおよび書き込み機能にパッチを適用します。

ソースリンクはここにあります

import configparser
import pytest

simpleconfig = """[section]\nkey = value\n\n"""

def test_monkeypatch_open_read(mockopen):
    filename = 'somefile.txt'
    mockopen.write(filename, simpleconfig)
 
    parser = configparser.ConfigParser()
    parser.read(filename)
    assert parser.sections() == ['section']
 
def test_monkeypatch_open_write(mockopen):
    parser = configparser.ConfigParser()
    parser.add_section('section')
    parser.set('section', 'key', 'value')
 
    filename = 'somefile.txt'
    parser.write(open(filename, 'wb'))
    assert mockopen.read(filename) == simpleconfig
于 2021-02-16T00:54:22.893 に答える
-2

アサート付きのシンプルな@patch

@patchを使用したい場合。open()はハンドラー内で呼び出され、読み取られます。

    @patch("builtins.open", new_callable=mock_open, read_data="data")
    def test_lambda_handler(self, mock_open_file):
        
        lambda_handler(event, {})
于 2021-04-01T19:55:40.560 に答える