Bir ithalat nasıl takılır?


144

Modül Aiçerir import Bonun üstünde. Ancak deney koşullarında İsterdim alay B içinde A(mock A.B) ve tamamen ithal kaçınmaya B.

Aslında, Btest ortamına bilerek kurulmamıştır.

Atest edilen birimdir. ATüm işlevselliğiyle içe aktarmak zorundayım . Btaklit etmem gereken modül. Ama nasıl taklit Biçinde Ave durdurma Agerçek ithal gelen Bilk şey ise, Aithalat olduğunu mu B?

(B'nin yüklenmemesinin nedeni hızlı test için pypy kullanmam ve maalesef B henüz pypy ile uyumlu olmamasıdır.)

Bu nasıl yapılabilir?

Yanıtlar:


134

İstediğinizi almak için sys.modules['B']içe aktarmadan önce atayabilirsiniz A:

test.py :

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

print(A.B.__name__)

A.py :

import B

Not B.py mevcut değildir, ancak çalışırken test.pyhiçbir hata döndürülmez ve print(A.B.__name__)yazdırılır mock_B. Hala mock_B.pyalay Bgerçek fonksiyonları / değişkenleri / vb bir yerde oluşturmak zorunda . Veya Mock()doğrudan bir doğrudan atayabilirsiniz :

test.py :

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

3
Mock( __%s__) gibi bazı sihirli özellikleri yamalamayacağını unutmayın __name__.
reclosedev

7
@reclosedev - bunun için Magic Mock var
Jonathan

2
B içe aktarma işleminin tekrar ImportError olması için bunu nasıl geri alırsınız? Denedim sys.modules['B'] = Noneama işe yaramıyor.
audiodude

2
Diğer birim test dosyalarının alay edilen nesneden etkilenmemesi için testin sonunda bu alay konusu içe aktarmayı nasıl sıfırlarsınız?
Riya John

1
Anlaşılır olması için, cevabı gerçekten içe aktarmanız mockve ardından çağırmanız gerekirmock.Mock()
nmz787

28

Yerleşik __import__, daha fazla kontrol için 'sahte' kütüphane ile alay edilebilir:

# 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

Diyelim ki Aşöyle görünüyor:

import B

def a():
    return B.func()

A.a()b_mock.func()Ayrıca alay edilebilir döner .

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

Python 3 Not: belirtildiği gibi 3.0 için değişmek , __builtin__şimdi adlandırılır builtins:

Modül __builtin__olarak yeniden adlandırıldı builtins(alt çizgileri kaldırarak, bir 's' ekleyerek).

Değiştirmeye bu yanıtında kod cezası işleri __builtin__tarafından builtinsPython 3 için.


1
Bu çalışmayı onaylayan var mı? Görüyorum import_mockçağrısında get import Aancak ithal ettiği her şey için.
Jonathon Reinhart

3
Python 3.4.3 ile alıyorumImportError: No module named '__builtin__'
Lucas Cimon

ithal etmek zorunda__builtin__
Aidenhjj

1
@LucasCimon yerine __builtin__göre builtinspython3 için ( docs.python.org/3/whatsnew/3.0.html?highlight=__builtin__ )
Luke Marlin

17

Bir ithalat alay etmek nasıl, (sahte AB)?

Modül A, üst kısmında B ithalatını içerir.

Kolay, sadece ithal edilmeden önce sys.modules içindeki kütüphaneyi alay et:

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

ve sonra, AB'nin nesnelerinden döndürülen belirli veri türlerine dayanmadığı sürece :

import A

sadece çalışmalı.

Ayrıca alay edebilirsiniz import A.B:

Bu, alt modülleriniz olsa bile çalışır, ancak her modülü taklit etmek istersiniz. Diyelim ki:

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

Alay etmek için, yukarıdakileri içeren modül içe aktarılmadan önce aşağıdakileri yapmanız yeterlidir:

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

(Deneyimlerim: Tek bir platformda çalışan bir bağımlılığım vardı, Windows, ancak günlük testlerimizi yaptığımız Linux'ta çalışmadı. Bu yüzden testlerimiz için bağımlılığı alay etmeliydim. Neyse ki bu kara bir kutuydu, bu yüzden Çok fazla etkileşim kurmam gerekmedi.)

Alaycı Yan Etkiler

Zeyilname: Aslında biraz zaman alan bir yan etkiyi simüle etmem gerekiyordu. Bu yüzden bir saniye uyumak için bir nesnenin yöntemine ihtiyacım vardı. Bu şöyle çalışır:

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)

Ve sonra kodun çalıştırılması biraz zaman alıyor, tıpkı gerçek yöntem gibi.


7

Buradaki partiye biraz geç kaldığımı anlıyorum, ancak mockkütüphane ile bunu otomatikleştirmenin çılgınca bir yolu var :

(işte örnek bir kullanım)

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

Bunun bu kadar gülünç derecede karmaşık olmasının nedeni, bir içe aktarmanın python'un temelde bunu yapmasıdır (örneğin from herp.derp import foo)

  1. Var mı sys.modules['herp']? Başka içe aktarın. Hala değilseImportError
  2. Var mı sys.modules['herp.derp']? Başka içe aktarın. Hala değilseImportError
  3. Niteliğini alın fooait sys.modules['herp.derp']. BaşkaImportError
  4. foo = sys.modules['herp.derp'].foo

Bu saldırıya uğramış çözümün bazı dezavantajları var: Modül yolundaki başka şeylere güvenen başka bir şey varsa, bu tür vidalar. Ayrıca bu yalnızca satır içi olarak içe aktarılan öğeler için de kullanılabilir.

def foo():
    import herp.derp

veya

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

7

Aaron Hall'un cevabı benim için çalışıyor. Sadece önemli bir şeyden bahsetmek istiyorum,

eğer A.pyyaparsan

from B.C.D import E

o zaman test.pyyol boyunca her modülü taklit etmelisiniz, aksi haldeImportError

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

4

Python'daki ithalatla alay etmenin iyi bir yolunu buldum. Bu var Eric'in Zaadi çözüm bulunana burada sadece benim içeride kullandıkları Django uygulaması.

Ben sınıf var SeatInterfacearayüzüdür Seatmodeli sınıfında. Yani seat_interfacemodülümün içinde böyle bir ithalat var:

from ..models import Seat

class SeatInterface(object):
    (...)

Ben SeatInterfacealay Seatsınıf ile sınıf için izole testler oluşturmak istedim FakeSeat. Sorun şu ki - tu run Django uygulamasının kapalı olduğu çevrimdışı testleri nasıl. Aşağıdaki hatayla karşılaştım:

Yanlış Yapılandırılmış: İstenen ayar BASE_DIR, ancak ayarlar yapılandırılmamış. Ayarlara erişmeden önce DJANGO_SETTINGS_MODULE ortam değişkenini tanımlamanız veya settings.configure () öğesini çağırmanız gerekir.

0.078s içinde 1 test koştu

BAŞARISIZ (hatalar = 1)

Çözüm şuydu:

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

Ve sonra test sihirli çalışır Tamam çalışır :)

.
1 testi 0.002s'de koştu

tamam


3

Bunu yaparsanız import ModuleB, yerleşik yöntemi gerçekten şöyle çağırıyorsunuz __import__:

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

__builtin__Modülü içe aktararak ve __builtin__.__import__yöntemin etrafına bir sarıcı yaparak bu yöntemin üzerine yazabilirsiniz . Veya modülden NullImporterkanca ile oynayabilirsiniz imp. İstisna yakalamak ve except-block içinde modül / sınıf alay.

İlgili dokümanlara gösterici:

docs.python.org: __import__

İmp Modülü ile İçe Aktarma dahili cihazlarına erişme

Umarım bu yardımcı olur. Be ÇOK sen piton programlamanın daha gizli perimeters içine ve gerçekten başarmak ve b) etkileri anlayışa istediğinizi a) Katı anlayış önemli olduğunu adım olduğunu adviced.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.