मेरे पास एक साधारण ज्ञापन है जिसका उपयोग मैं महंगा नेटवर्क कॉल के आसपास कुछ समय बचाने के लिए कर रहा हूं। मोटे तौर पर, मेरे कोड इस तरह दिखता है:ज्ञापन कार्यों का परीक्षण कैसे किया जा सकता है?
# mem.py
import functools
import time
def memoize(fn):
"""
Decorate a function so that it results are cached in memory.
>>> import random
>>> random.seed(0)
>>> f = lambda x: random.randint(0, 10)
>>> [f(1) for _ in range(10)]
[9, 8, 4, 2, 5, 4, 8, 3, 5, 6]
>>> [f(2) for _ in range(10)]
[9, 5, 3, 8, 6, 2, 10, 10, 8, 9]
>>> g = memoize(f)
>>> [g(1) for _ in range(10)]
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
>>> [g(2) for _ in range(10)]
[8, 8, 8, 8, 8, 8, 8, 8, 8, 8]
"""
cache = {}
@functools.wraps(fn)
def wrapped(*args, **kwargs):
key = args, tuple(sorted(kwargs))
try:
return cache[key]
except KeyError:
cache[key] = fn(*args, **kwargs)
return cache[key]
return wrapped
def network_call(user_id):
time.sleep(1)
return 1
@memoize
def search(user_id):
response = network_call(user_id)
# do stuff to response
return response
और मैं इस कोड है, जहां मैं network_call()
के विभिन्न वापसी मान बाहर नकली यकीन है कि कुछ संशोधनों मैं search()
काम में क्या अपेक्षा के अनुरूप बनाने के लिए के लिए परीक्षण किया है।
import mock
import mem
@mock.patch('mem.network_call')
def test_search(mock_network_call):
mock_network_call.return_value = 2
assert mem.search(1) == 2
@mock.patch('mem.network_call')
def test_search_2(mock_network_call):
mock_network_call.return_value = 3
assert mem.search(1) == 3
हालांकि, जब मैं इन परीक्षणों चलाने के लिए, मैं एक विफलता क्योंकि search()
एक कैश्ड परिणाम देता है मिलता है।
CAESAR-BAUTISTA:~ caesarbautista$ py.test test_mem.py
============================= test session starts ==============================
platform darwin -- Python 2.7.8 -- py-1.4.26 -- pytest-2.6.4
collected 2 items
test_mem.py .F
=================================== FAILURES ===================================
________________________________ test_search_2 _________________________________
args = (<MagicMock name='network_call' id='4438999312'>,), keywargs = {}
extra_args = [<MagicMock name='network_call' id='4438999312'>]
entered_patchers = [<mock._patch object at 0x108913dd0>]
exc_info = (<class '_pytest.assertion.reinterpret.AssertionError'>, AssertionError(u'assert 2 == 3\n + where 2 = <function search at 0x10893f848>(1)\n + where <function search at 0x10893f848> = mem.search',), <traceback object at 0x1089502d8>)
patching = <mock._patch object at 0x108913dd0>
arg = <MagicMock name='network_call' id='4438999312'>
@wraps(func)
def patched(*args, **keywargs):
# don't use a with here (backwards compatability with Python 2.4)
extra_args = []
entered_patchers = []
# can't use try...except...finally because of Python 2.4
# compatibility
exc_info = tuple()
try:
try:
for patching in patched.patchings:
arg = patching.__enter__()
entered_patchers.append(patching)
if patching.attribute_name is not None:
keywargs.update(arg)
elif patching.new is DEFAULT:
extra_args.append(arg)
args += tuple(extra_args)
> return func(*args, **keywargs)
/opt/boxen/homebrew/lib/python2.7/site-packages/mock.py:1201:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
mock_network_call = <MagicMock name='network_call' id='4438999312'>
@mock.patch('mem.network_call')
def test_search_2(mock_network_call):
mock_network_call.return_value = 3
> assert mem.search(1) == 3
E assert 2 == 3
E + where 2 = <function search at 0x10893f848>(1)
E + where <function search at 0x10893f848> = mem.search
test_mem.py:15: AssertionError
====================== 1 failed, 1 passed in 0.03 seconds ======================
क्या ज्ञापन कार्यों का परीक्षण करने का कोई तरीका है? मैंने कुछ विकल्प माना है लेकिन उनमें से प्रत्येक को कमियां हैं।
एक समाधान memoize()
नकल करना है। मैं ऐसा करने में अनिच्छुक हूं क्योंकि यह परीक्षणों के कार्यान्वयन विवरण को रिसाव करता है। सैद्धांतिक रूप से, मैं एक कार्यात्मक दृष्टिकोण से ध्यान देने, परीक्षण सहित, शेष प्रणाली के बिना कार्यों को याद और अनमोल करने में सक्षम होना चाहिए।
सजावट समारोह का पर्दाफाश करने के लिए कोड को फिर से लिखना एक और समाधान है। यही कारण है, मैं कुछ इस तरह कर सकता है:,
def _search(user_id):
return network_call(user_id)
search = memoize(_search)
बहरहाल, यह एक ही समस्याओं में के रूप में ऊपर चलाता है, हालांकि यह यकीनन बदतर है क्योंकि यह पुनरावर्ती कार्यों के लिए काम नहीं करेगा है।
सुनिश्चित नहीं हैं कि मैं समझता हूँ। अलग-अलग रिटर्न मूल्यों के लिए आपके पास दो परीक्षण परीक्षण क्यों हैं? यदि आपके ज्ञात फ़ंक्शन के लिए "बाँध" मान वापस करने के लिए ठीक है (नेटवर्क से लाइव मान के समान नहीं), तो आपको दो मानों का परीक्षण नहीं करना चाहिए। यदि यह ठीक नहीं है, तो आपको अपने ज्ञापन को अधिक परिष्कृत बनाने की आवश्यकता है ताकि जब भी आवश्यक हो तो आप किसी भी तरह कैश को अमान्य कर सकते हैं। याद रखने में कोई उपयोग नहीं है यदि आपके पास यह जानने का कोई तरीका नहीं है कि ज्ञात मूल्य बनाम वास्तविक मूल्य प्राप्त करने के लिए ठीक है। – BrenBarn
मुझे समझ में नहीं आता कि आपका प्रस्तावित समाधान क्यों काम नहीं करेगा। क्या आप अपने परीक्षणों में सिर्फ '_search' का उपयोग नहीं कर सकते? मेरी धारणा यह है कि जब आप नेटवर्क कॉल विभिन्न मान देता है, तो आप '_search' के व्यवहार का परीक्षण करना चाहते हैं, और ज्ञापन में रूचि नहीं रखते हैं। –
एचएम, आपको पीछा करने में परेशानी है। परीक्षणों में 'network_call()' अनुकरण करने के लिए अलग-अलग रिटर्न मान होते हैं जिनके वापसी मान समान पैरामीटर के लिए भिन्न हो सकते हैं (यानी सर्वर पर मान के आधार पर)। उन्हें स्वतंत्र माना जाता है, इसलिए कैश को अमान्य करने की आवश्यकता नहीं होनी चाहिए। –