कैसे करें आयात का मजाक


144

मॉड्यूल अपने शीर्ष पर Aशामिल है import B। हालांकि परीक्षण परिस्थितियों में मैं करना चाहते हैं नकली B में A(नकली A.B) और पूरी तरह से आयात करने से बचना B

वास्तव में, Bउद्देश्य पर परीक्षण वातावरण में स्थापित नहीं है।

Aपरीक्षण के तहत इकाई है। मुझे Aइसकी सभी कार्यक्षमता के साथ आयात करना होगा। Bवह मॉड्यूल है जिसका मुझे मखौल उड़ाना होगा। लेकिन मैं कैसे नकली कर सकते हैं Bके भीतर Aऔर बंद Aवास्तविक आयात करने से B, अगर पहली बात Aकरता आयात है B?

(कारण बी स्थापित नहीं है कि मैं त्वरित परीक्षण के लिए pypy का उपयोग करता हूं और दुर्भाग्य से B अभी तक pypy के साथ संगत नहीं है।)

यह कैसे किया जा सकता है?

जवाबों:


134

आप जो चाहते हैं उसे पाने के लिए sys.modules['B']आयात Aकरने से पहले असाइन कर सकते हैं:

test.py :

import sys
sys.modules['B'] = __import__('mock_B')
import A

print(A.B.__name__)

एपी :

import B

नोट Bpy मौजूद नहीं है, लेकिन जब test.pyकोई त्रुटि नहीं होती है, तो उसे लौटाया जाता है और print(A.B.__name__)प्रिंट करता है mock_B। आपको अभी भी एक ऐसा स्थान बनाना है mock_B.pyजहाँ आप Bवास्तविक कार्यों / चर / आदि का मखौल उड़ाते हैं। या आप बस Mock()सीधे असाइन कर सकते हैं:

test.py :

import sys
sys.modules['B'] = Mock()
import A

3
ध्यान रखें कि Mockकुछ जादुई विशेषताओं ( __%s__) को पैच नहीं करेंगे __name__
पुनरावृत्ति

7
@reclosedev - उसके लिए मैजिक मॉक है
जोनाथन

2
आप इसे पूर्ववत कैसे करते हैं ताकि B आयात फिर से एक आयात हो? मैंने कोशिश की, sys.modules['B'] = Noneलेकिन यह काम नहीं कर रहा है।
ऑडीओडायड

2
आप परीक्षण के अंत में इस नकली आयात को कैसे रीसेट कर सकते हैं, ताकि अन्य इकाई परीक्षण फाइलें नकली वस्तु से प्रभावित न हों?
रिया जॉन

1
स्पष्टता के लिए, आपको वास्तव में आयात करने mockऔर फिर कॉल करने का उत्तर संपादित करना चाहिएmock.Mock()
nmz787

28

Builtin __import__अधिक नियंत्रण के लिए 'नकली' पुस्तकालय के साथ मज़ाक उड़ाया जा सकता है:

# Store original __import__
orig_import = __import__
# This will be the B module
b_mock = mock.Mock()

def import_mock(name, *args):
    if name == 'B':
        return b_mock
    return orig_import(name, *args)

with mock.patch('__builtin__.__import__', side_effect=import_mock):
    import A

कहते Aहैं:

import B

def a():
    return B.func()

A.a()रिटर्न b_mock.func()जो मॉकड्रिल भी किया जा सकता है।

b_mock.func.return_value = 'spam'
A.a()  # returns 'spam'

पायथन 3 के लिए ध्यान दें: जैसा कि 3.0 के लिए चैंज में कहा गया है , __builtin__अब इसका नाम है builtins:

मॉड्यूल __builtin__का नाम बदलकर builtins(अंडरस्कोर हटाकर, 's' जोड़ दिया गया)।

इस जवाब में कोड ठीक काम करता है अगर आप को बदलने के __builtin__द्वारा builtinsअजगर 3 के लिए।


1
क्या किसी ने इस काम की पुष्टि की है? मैं देख रहा हूँ के import_mockलिए कहा जाता है import A, लेकिन यह आयात कुछ के लिए नहीं।
जोनाथन रेनहार्ट

3
पायथॉन 3.4.3 के साथ मुझे मिलता हैImportError: No module named '__builtin__'
लुकास साइमन

आपको आयात करना होगा__builtin__
Aidenhjj

1
@LucasCimon, की जगह __builtin__द्वारा builtinspython3 के लिए ( docs.python.org/3/whatsnew/3.0.html?highlight=__builtin__ )
ल्यूक मार्लिन

17

कैसे एक आयात नकली करने के लिए, (नकली एबी)?

मॉड्यूल A में इसके शीर्ष पर आयात B शामिल है।

आयात करने से पहले आसान, बस लाइब्रेरी को sys.modules में मॉक करें:

if wrong_platform():
    sys.modules['B'] = mock.MagicMock()

और फिर, तब तक जब तक कि AB के ऑब्जेक्ट्स से वापस आ रहे विशिष्ट प्रकार के डेटा पर निर्भर न हों:

import A

बस काम करना चाहिए।

आप यह भी नकली कर सकते हैं import A.B:

यहां तक ​​कि अगर आपके पास सबमॉड्यूल है, तो भी यह काम करता है, लेकिन आप प्रत्येक मॉड्यूल का मजाक बनाना चाहेंगे। कहो कि आपके पास यह है:

from foo import This, That, andTheOtherThing
from foo.bar import Yada, YadaYada
from foo.baz import Blah, getBlah, boink

मज़ाक करने के लिए, नीचे दिए गए मॉड्यूल को आयात करने से पहले बस नीचे करें:

sys.modules['foo'] = MagicMock()
sys.modules['foo.bar'] = MagicMock()
sys.modules['foo.baz'] = MagicMock()

(मेरा अनुभव: मेरी एक निर्भरता थी जो एक मंच पर काम करती है, विंडोज, लेकिन लिनक्स पर काम नहीं किया, जहां हम अपने दैनिक परीक्षण चलाते हैं। इसलिए मुझे अपने परीक्षणों के लिए निर्भरता का मज़ाक उड़ाने की ज़रूरत है। सौभाग्य से यह एक ब्लैक बॉक्स था, इसलिए मुझे बहुत अधिक सहभागिता स्थापित करने की आवश्यकता नहीं थी।)

मॉकिंग साइड इफेक्ट्स

परिशिष्ट: वास्तव में, मुझे कुछ समय लगने वाले साइड-इफेक्ट का अनुकरण करने की आवश्यकता थी। इसलिए मुझे एक सेकंड के लिए सोने के लिए एक ऑब्जेक्ट विधि की आवश्यकता थी। यह इस तरह काम करेगा:

sys.modules['foo'] = MagicMock()
sys.modules['foo.bar'] = MagicMock()
sys.modules['foo.baz'] = MagicMock()
# setup the side-effect:
from time import sleep

def sleep_one(*args): 
    sleep(1)

# this gives us the mock objects that will be used
from foo.bar import MyObject 
my_instance = MyObject()
# mock the method!
my_instance.method_that_takes_time = mock.MagicMock(side_effect=sleep_one)

और फिर कोड को वास्तविक विधि की तरह चलने में कुछ समय लगता है।


7

मुझे लगता है कि मैं यहाँ पार्टी के लिए थोड़ा लेट हो गया हूँ, लेकिन यहाँ इसे साथ स्वचालित करने के लिए एक पागल तरीका है mock पुस्तकालय के :

(यहां एक उदाहरण उपयोग है)

import contextlib
import collections
import mock
import sys

def fake_module(**args):
    return (collections.namedtuple('module', args.keys())(**args))

def get_patch_dict(dotted_module_path, module):
    patch_dict = {}
    module_splits = dotted_module_path.split('.')

    # Add our module to the patch dict
    patch_dict[dotted_module_path] = module

    # We add the rest of the fake modules in backwards
    while module_splits:
        # This adds the next level up into the patch dict which is a fake
        # module that points at the next level down
        patch_dict['.'.join(module_splits[:-1])] = fake_module(
            **{module_splits[-1]: patch_dict['.'.join(module_splits)]}
        )
        module_splits = module_splits[:-1]

    return patch_dict

with mock.patch.dict(
    sys.modules,
    get_patch_dict('herp.derp', fake_module(foo='bar'))
):
    import herp.derp
    # prints bar
    print herp.derp.foo

इसका कारण यह बहुत ही हास्यास्पद है कि जब कोई आयात करता है तो मूल रूप से अजगर ऐसा करता है (उदाहरण के लिए from herp.derp import foo)

  1. है sys.modules['herp']मौजूद हैं? इसे आयात करें। अगर फिर भी नहींImportError
  2. है sys.modules['herp.derp']मौजूद हैं? इसे आयात करें। अगर फिर भी नहींImportError
  3. विशेषता हासिल fooकी sys.modules['herp.derp']। अन्यImportError
  4. foo = sys.modules['herp.derp'].foo

इस हैक किए गए समाधान के लिए कुछ डाउनसाइड्स हैं: यदि मॉड्यूल पथ में अन्य सामान पर कुछ और निर्भर करता है तो इस तरह के शिकंजा खत्म हो जाते हैं। इसके अलावा यह केवल उस सामान के लिए काम करता है जिसे इनलाइन जैसे आयात किया जा रहा है

def foo():
    import herp.derp

या

def foo():
    __import__('herp.derp')

7

हारून हॉल का जवाब मेरे लिए काम करता है। बस एक महत्वपूर्ण बात का उल्लेख करना चाहता हूं,

अगर A.pyतुम में

from B.C.D import E

तो test.pyआप रास्ते में हर मॉड्यूल का मजाक उड़ाना चाहिए, अन्यथाImportError

sys.modules['B'] = mock.MagicMock()
sys.modules['B.C'] = mock.MagicMock()
sys.modules['B.C.D'] = mock.MagicMock()

4

मुझे पायथन में आयातों का मजाक उड़ाने का बढ़िया तरीका मिला। यह एरिक का ज़ैदी समाधान यहां पाया गया है जिसे मैं अपने Django एप्लिकेशन के अंदर उपयोग करता हूं ।

मुझे क्लास मिली है SeatInterfaceजो Seatमॉडल क्लास के लिए इंटरफ़ेस है । इसलिए मेरे seat_interfaceमॉड्यूल के अंदर मेरा ऐसा आयात है:

from ..models import Seat

class SeatInterface(object):
    (...)

मैं के लिए पृथक परीक्षण बनाना चाहता था SeatInterfaceमज़ाक उड़ाया साथ वर्ग Seatके रूप में वर्ग FakeSeat। समस्या यह थी - कैसे ट्यून ऑफ़लाइन ऑफ़लाइन परीक्षण, जहां Django आवेदन नीचे है। मेरे पास त्रुटि थी:

अनुचित तरीके से कॉन्फ़िगर किया गया: BASE_DIR सेटिंग की आवश्यकता है, लेकिन सेटिंग्स कॉन्फ़िगर नहीं की गई हैं। सेटिंग तक पहुँचने से पहले आपको या तो पर्यावरण चर को परिभाषित करना होगा DJANGO_SETTINGS_MODULE या कॉल सेटिंग .configure ()।

0.078 में रण 1 टेस्ट

विफल (त्रुटियाँ = 1)

समाधान था:

import unittest
from mock import MagicMock, patch

class FakeSeat(object):
    pass

class TestSeatInterface(unittest.TestCase):

    def setUp(self):
        models_mock = MagicMock()
        models_mock.Seat.return_value = FakeSeat
        modules = {'app.app.models': models_mock}
        patch.dict('sys.modules', modules).start()

    def test1(self):
        from app.app.models_interface.seat_interface import SeatInterface

और फिर जादुई रूप से परीक्षण ठीक चलता है :)


0.002s में रैन 1 परीक्षण

ठीक है


3

यदि आप ऐसा करते हैं तो import ModuleBआप वास्तव में बिलिन पद्धति __import__को बुला रहे हैं :

ModuleB = __import__('ModuleB', globals(), locals(), [], -1)

आप __builtin__मॉड्यूल आयात करके और __builtin__.__import__विधि के चारों ओर एक आवरण बनाकर इस पद्धति को अधिलेखित कर सकते हैं । या आप मॉड्यूल NullImporterसे हुक के साथ खेल सकते हैं imp। अपवाद को पकड़ने और अपने मॉड्यूल / वर्ग को मॉक करेंexcept -ब्लॉक में।

संबंधित डॉकों को इंगित करें:

docs.python.org: __import__

आयात मॉड्यूल के साथ छोटा सा भूत के साथ प्रवेश

आशा है कि ये आपकी मदद करेगा। जा अत्यधिक सलाह दी कि तुम अजगर प्रोग्रामिंग की अधिक रहस्यमय परिधि में और है कि एक) ठोस समझ क्या तुम सच में प्राप्त करने और ख के लिए) निहितार्थ के पूरी तरह से समझने चाहते महत्वपूर्ण है कदम।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.