Làm cách nào để kiểm tra mã sau bằng giả (sử dụng giả, trang trí bản vá và các câu lệnh được cung cấp bởi khung Mock của Michael Foord ):
def testme(filepath):
with open(filepath, 'r') as f:
return f.read()
Làm cách nào để kiểm tra mã sau bằng giả (sử dụng giả, trang trí bản vá và các câu lệnh được cung cấp bởi khung Mock của Michael Foord ):
def testme(filepath):
with open(filepath, 'r') as f:
return f.read()
Câu trả lời:
Cách thức thực hiện điều này đã thay đổi trong mock 0.7.0, cuối cùng hỗ trợ chế tạo các phương thức giao thức python (phương thức ma thuật), đặc biệt là sử dụng MagicMock:
http://www.voidspace.org.uk/python/mock/magicmock.html
Một ví dụ về giả định mở như một trình quản lý bối cảnh (từ trang ví dụ trong tài liệu giả):
>>> 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')
__enter__và __exit__giả định các đối tượng - là cách tiếp cận sau đã lỗi thời hay vẫn còn hữu ích?
fileđã biến mất!
mock_openlà một phần của mockkhung và rất đơn giản để sử dụng. patchđược sử dụng làm bối cảnh trả về đối tượng được sử dụng để thay thế đối tượng đã vá: bạn có thể sử dụng nó để làm cho thử nghiệm của mình đơn giản hơn.
Sử dụng builtinsthay vì __builtin__.
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")
mockkhông phải là một phần của unittestvà bạn nên vá__builtin__
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")
Nếu bạn sử dụng patchlàm trang trí bằng cách sử dụng mock_open()kết quả new patchcủa đối số thì có thể hơi lạ một chút.
Trong trường hợp này tốt hơn là sử dụng new_callable patchđối số và nhớ rằng mọi đối số bổ sung patchkhông sử dụng sẽ được chuyển sang new_callablechức năng như được mô tả trong patchtài liệu .
patch () lấy các đối số từ khóa tùy ý. Chúng sẽ được chuyển đến Mock (hoặc new_callable) khi xây dựng.
Ví dụ phiên bản trang trí cho Python 3.x là:
@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")
Hãy nhớ rằng trong trường hợp patchnày sẽ thêm đối tượng giả làm đối số của hàm kiểm tra của bạn.
with patch("builtins.open", mock_open(read_data="data")) as mock_file:thể chuyển đổi thành cú pháp trang trí không? Tôi đã thử, nhưng tôi không chắc những gì tôi cần chuyển vào @patch("builtins.open", ...) làm đối số thứ hai.
return_valuecủa mock_openvào một đối tượng giả và khẳng định các mô hình thứ hai return_value), nhưng nó làm việc bằng cách thêm mock_opennhư new_callable.
sixmô-đun để có một mockmô-đun nhất quán . Nhưng tôi không biết nếu nó cũng ánh xạ builtinstrong một mô-đun chung.
Với các phiên bản mới nhất của giả, bạn có thể sử dụng trình trợ giúp mock_open thực sự hữu ích :
mock_open (mock = Không, read_data = Không)
Một chức năng trợ giúp để tạo ra một giả để thay thế việc sử dụng mở. Nó hoạt động để mở được gọi trực tiếp hoặc được sử dụng như một trình quản lý bối cảnh.
Đối số giả là đối tượng giả để cấu hình. Nếu Không có (mặc định) thì MagicMock sẽ được tạo cho bạn, với API giới hạn ở các phương thức hoặc thuộc tính có sẵn trên tay cầm tệp tiêu chuẩn.
read_data là một chuỗi cho phương thức đọc của tệp xử lý trả về. Đây là một chuỗi rỗng theo mặc định.
>>> 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')
.writecuộc gọi?
handle.write.assert_any_call(). Bạn cũng có thể sử dụng handle.write.call_args_listđể nhận từng cuộc gọi nếu đơn hàng quan trọng.
m.return_value.write.assert_called_once_with('some stuff')là tốt hơn imo. Tránh đăng ký một cuộc gọi.
Mock.call_args_listlà an toàn hơn so với việc gọi bất kỳ Mock.assert_xxxphương thức nào. Nếu bạn đánh vần sai bất kỳ từ nào sau này, là thuộc tính của Mock, chúng sẽ luôn âm thầm vượt qua.
Để sử dụng mock_open cho một tệp đơn giản read()(đoạn mã mock_open ban đầu đã được cung cấp trên trang này được định hướng nhiều hơn để ghi):
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()
Lưu ý theo tài liệu cho mock_open, điều này đặc biệt dành cho read(), vì vậy sẽ không hoạt động với các mẫu phổ biến như for line in f, chẳng hạn.
Sử dụng trăn 2.6.6 / mock 1.0.1
for line in opened_file:loại mã. Tôi đã thử trải nghiệm với StringIO có thể lặp lại thực hiện __iter__và sử dụng nó thay vì my_text, nhưng không có may mắn.
read()vì vậy sẽ không hoạt động trong for line in opened_filetrường hợp của bạn ; Tôi đã chỉnh sửa bài đăng để làm rõ
for line in f:hỗ trợ có thể đạt được bằng cách chế giễu giá trị trả về open()là một đối tượng StringIO thay .
with open("any_string") as f: print f.read()
Câu trả lời hàng đầu là hữu ích nhưng tôi đã mở rộng về nó một chút.
Nếu bạn muốn đặt giá trị của đối tượng tệp của mình (phần ftrong as f) dựa trên các đối số được truyền cho open()đây, một cách để thực hiện:
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
Về cơ bản, open()sẽ trả về một đối tượng và withsẽ gọi __enter__()đối tượng đó.
Để chế nhạo đúng cách, chúng ta phải giả lập open()để trả về một đối tượng giả. Đối tượng giả đó sau đó sẽ mô phỏng __enter__()cuộc gọi trên nó ( MagicMocksẽ thực hiện điều này cho chúng tôi) để trả về đối tượng dữ liệu / tệp giả mà chúng tôi muốn (do đó mm.__enter__.return_value). Làm điều này với 2 giả theo cách trên cho phép chúng tôi nắm bắt các đối số được truyền đến open()và chuyển chúng sang do_something_with_dataphương thức của chúng tôi .
Tôi đã chuyển toàn bộ tệp giả thành một chuỗi open()và tôi do_something_with_datatrông như thế này:
def do_something_with_data(*args, **kwargs):
return args[0].split("\n")
Điều này biến đổi chuỗi thành một danh sách để bạn có thể thực hiện như sau với một tệp bình thường:
for line in file:
#do action
__enter__? Nó chắc chắn trông giống như một hack hơn là một cách được đề xuất.
Tôi có thể hơi trễ trò chơi, nhưng điều này hiệu quả với tôi khi gọi opentrong một mô-đun khác mà không phải tạo một tệp mới.
kiểm tra
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")
Bằng cách vá openchức năng bên trong __builtin__mô-đun thành của tôi mock_open(), tôi có thể giả định ghi vào một tệp mà không cần tạo.
Lưu ý: Nếu bạn đang sử dụng một mô-đun sử dụng cython hoặc chương trình của bạn phụ thuộc vào cython theo bất kỳ cách nào, bạn sẽ cần nhập mô-đun của cython__builtin__ bằng cách bao gồm import __builtin__ở đầu tệp của bạn. Bạn sẽ không thể chế giễu phổ quát __builtin__nếu bạn đang sử dụng cython.
import __builtin__vào mô-đun thử nghiệm của tôi. Bài viết này đã giúp làm rõ lý do tại sao kỹ thuật này hoạt động tốt như nó: ichimonji10.name/blog/6
Điều này làm việc cho một bản vá để đọc một cấu hình json.
class ObjectUnderTest:
def __init__(self, filename: str):
with open(filename, 'r') as f:
dict_content = json.load(f)
Đối tượng bị giả là đối tượng io.TextIOWrapper được trả về bởi hàm open ()
@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):
Nếu bạn không cần thêm bất kỳ tập tin nào, bạn có thể trang trí phương pháp kiểm tra:
@patch('builtins.open', mock_open(read_data="data"))
def test_testme():
result = testeme()
assert result == "data"