Aşağıdaki kodu alaylarla nasıl test edebilirim (alay, yama dekoratörü ve Michael Foord'un Mock çerçevesi tarafından sağlanan nöbetçi kullanarak ):
def testme(filepath):
with open(filepath, 'r') as f:
return f.read()
Aşağıdaki kodu alaylarla nasıl test edebilirim (alay, yama dekoratörü ve Michael Foord'un Mock çerçevesi tarafından sağlanan nöbetçi kullanarak ):
def testme(filepath):
with open(filepath, 'r') as f:
return f.read()
Yanıtlar:
Bunu yapmanın yolu, özellikle MagicMock'u kullanarak python protokol yöntemlerini (sihirli yöntemler) taklit etmeyi destekleyen sahte 0.7.0'da değişti:
http://www.voidspace.org.uk/python/mock/magicmock.html
Bağlam yöneticisi olarak açık alaycı örneği (alay dokümanındaki örnekler sayfasından):
>>> 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__
ve __exit__
nesneleri açıkça ayarlayan ve alay eden bağlam yöneticisi örneğinden çok daha basit görünüyor - ikinci yaklaşım güncel değil mi, yoksa hala faydalı mı?
file
gitti!
mock_open
mock
çerçevenin bir parçasıdır ve kullanımı çok basittir. patch
bağlam olarak kullanıldığında yamalı olanı değiştirmek için kullanılan nesneyi döndürür: bunu testinizi basitleştirmek için kullanabilirsiniz.
Yerine builtins
kullanın __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")
mock
bir parçası değil unittest
ve yama yapmalısın__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")
Eğer patch
dekoratör olarak mock_open()
's sonucunu kullanarak kullanırsanız new
patch
argümanı biraz tuhaf olabilir.
Bu durumda, new_callable
patch
'argümanını kullanmak ve kullanmayan her fazladan argümanın dokümantasyonda açıklandığı gibi işlev patch
göreceğini hatırlamak daha iyidir .new_callable
patch
patch (), rastgele anahtar kelime bağımsız değişkenleri alır. Bunlar inşaatta Mock'a (veya new_callable) aktarılacaktır.
Örneğin Python 3.x için dekore edilmiş sürüm :
@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")
Bu durumda patch
sahte nesneyi test fonksiyonunuzun argümanı olarak ekleyeceğini unutmayın .
with patch("builtins.open", mock_open(read_data="data")) as mock_file:
dekoratör sözdizimine dönüştürülebilir mi? Denedim, ama @patch("builtins.open", ...)
ikinci argüman olarak neye girmem gerektiğinden emin değilim .
return_value
bir mock_open
başka sahte nesnesine ve ikinci alay en assert return_value
), ancak ekleyerek çalıştı mock_open
olarak new_callable
.
six
, tutarlı bir mock
modüle sahip olmak için modüle bakın. Ama builtins
ortak bir modülde de eşleşip eşlenmediğini bilmiyorum .
Mock'un en son sürümleriyle, gerçekten yararlı mock_open yardımcısını kullanabilirsiniz:
mock_open (mock = Yok, read_data = Yok)
Açık kullanım yerine alay oluşturmak için bir yardımcı işlevi. Doğrudan çağrılan veya içerik yöneticisi olarak kullanılan açık işler için çalışır.
Sahte argüman yapılandırılacak sahte nesnedir. Hiçbiri (varsayılan) değilse, API standart dosya tanıtıcılarında kullanılabilen yöntem veya özniteliklerle sınırlı olacak şekilde bir MagicMock oluşturulacaktır.
read_data, dosya tanıtıcısının okuma yönteminin döndüreceği bir dizedir. Bu, varsayılan olarak boş bir dizedir.
>>> 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')
.write
çağrı olup olmadığını nasıl kontrol edersiniz ?
handle.write.assert_any_call()
. handle.write.call_args_list
Sipariş önemliyse her aramayı almak için de kullanabilirsiniz .
m.return_value.write.assert_called_once_with('some stuff')
daha iyi imo. Çağrı kaydetmekten kaçınır.
Mock.call_args_list
etmek Mock.assert_xxx
, yöntemlerden herhangi birini çağırmaktan daha güvenlidir . Eğer Mock'un nitelikleri olan ikincisini yanlış hecelerseniz, her zaman sessizce geçerler.
Basit bir dosya için mock_open kullanmak için read()
( bu sayfada zaten verilen orijinal mock_open snippet'i daha çok yazmaya yöneliktir):
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()
Not: mock_open için dokümanlara göre, bu özellikle içindir read()
, bu nedenle for line in f
örneğin ortak kalıplarla çalışmaz .
Python 2.6.6 / alay 1.0.1 kullanır
for line in opened_file:
kod türüyle çalışmak için alamıyorum . Ben uygular tekrarlanabilir StringIO ile denemeye çalıştı __iter__
ve bunun yerine my_text
, ama şans yok kullanarak .
read()
sizin for line in opened_file
durumunuz için çalışmaz ;
for line in f:
desteği bunun yerine bir StringIO nesnesiopen()
olarak dönüş değeri alay edilerek elde edilebilir .
with open("any_string") as f: print f.read()
En iyi cevap faydalı ama biraz daha genişledim.
Dosya nesnenizin ( f
in as f
) değerini, iletilen bağımsız değişkenlere dayanarak ayarlamak istiyorsanız open()
, bunu yapmanın bir yolu vardır:
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
Temel olarak, open()
bir nesneyi döndürür ve o nesneyi with
çağırır __enter__()
.
Düzgün alay open()
etmek için, sahte bir nesne döndürmek için alay etmeliyiz . Bu sahte nesne, daha sonra istediğimiz sahte veri / dosya nesnesini (dolayısıyla ) döndürmek için __enter__()
çağrıyı alay etmelidir ( MagicMock
bunu bizim için yapar mm.__enter__.return_value
). Bunu yukarıdaki şekilde 2 alayla yapmak, geçirilen argümanları yakalamamızı ve yöntemimize open()
aktarmamızı sağlar do_something_with_data
.
Bir dize olarak tüm sahte bir dosyayı geçti open()
ve benim do_something_with_data
gibi görünüyordu:
def do_something_with_data(*args, **kwargs):
return args[0].split("\n")
Bu, dizeyi bir listeye dönüştürür, böylece normal bir dosyada yaptığınız gibi aşağıdakileri yapabilirsiniz:
for line in file:
#do action
__enter__
? Kesinlikle önerilen bir yol daha bir kesmek gibi görünüyor.
Oyuna biraz geç kalmış olabilirim, ancak open
yeni bir dosya oluşturmak zorunda kalmadan başka bir modülü çağırırken bu benim için çalıştı .
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")
Modülün open
içindeki işlevi yamaya ekleyerek, bir dosya oluşturmadan bir dosyaya yazabilirim.__builtin__
mock_open()
Not: cython kullanan bir modül kullanıyorsanız veya programınız herhangi bir şekilde cython'a bağlıysa, dosyanızın üstüne dahil ederek cython __builtin__
modülünü içe aktarmanız gerekir import __builtin__
. Eğer __builtin__
cython kullanıyorsanız evrensel alay ile alay edemezsiniz .
import __builtin__
Test modülüme eklediğimden emin olmalıydım. Bu makale, bu tekniğin neden iyi çalıştığını açıklığa kavuşturmaya yardımcı oldu: ichimonji10.name/blog/6
Bu, bir düzeltme ekinin bir json yapılandırmasını okuması için çalıştı.
class ObjectUnderTest:
def __init__(self, filename: str):
with open(filename, 'r') as f:
dict_content = json.load(f)
Alaycı nesne, open () işlevi tarafından döndürülen io.TextIOWrapper nesnesidir
@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):
Daha fazla dosyaya ihtiyacınız yoksa test yöntemini dekore edebilirsiniz:
@patch('builtins.open', mock_open(read_data="data"))
def test_testme():
result = testeme()
assert result == "data"