수입을 조롱하는 방법


143

모듈 상단에 A포함되어 있습니다 import B. 그러나 시험 조건 하에서 내가하고 싶은 조롱 BA(모의 A.B) 완전히 가져 삼가 B.

실제로 B는 테스트 환경에 의도적으로 설치되지 않습니다.

A테스트중인 단위입니다. A모든 기능 을 가져와야 합니다. B내가 조롱해야 할 모듈입니다. 그러나 어떻게 조롱 수 있습니다 BA및 정지 A실제를 가져 오기에서 B제일 먼저하는 경우, A수입이다합니까 B?

(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__)

A.py :

import B

참고 B.py는 존재하지 않지만 실행 중에는 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__.
reclosedev

7
@reclosedev – 그것에 대한 마술 모의 가 있습니다
Jonathan

2
B 가져 오기가 다시 ImportError가되도록이 작업을 어떻게 취소합니까? 시도 sys.modules['B'] = None했지만 작동하지 않는 것 같습니다.
audiodude

2
테스트가 끝날 때이 모의 가져 오기를 어떻게 재설정하여 다른 단위 테스트 파일이 모의 객체에 의해 영향을받지 않게합니까?
리야 존

1
명확하게하기 위해 실제로 가져 와서 mock전화를 걸 려면 응답을 편집해야합니다.mock.Mock()
nmz787

28

__import__더 많은 제어를 위해 'mock'라이브러리로 내장 을 조롱 할 수 있습니다.

# 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하지만 가져 오는 것이 아닙니다.
Jonathon Reinhart

3
Python 3.4.3을 사용하면 다음과 같이 나타납니다.ImportError: No module named '__builtin__'
Lucas Cimon

수입해야합니다__builtin__
Aidenhjj

1
@LucasCimon 대체 __builtin__하여 builtinspython3 위해 ( docs.python.org/3/whatsnew/3.0.html?highlight=__builtin__을 )
루크 멀린에게

17

수입을 조롱하는 방법, (모의 AB)?

모듈 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()

(내 경험 : Windows 하나의 플랫폼에서는 작동하지만 매일 테스트를 실행하는 Linux에서는 작동하지 않는 종속성이 있었으므로 테스트를 위해 종속성을 조롱해야했습니다. 다행히도 블랙 박스였습니다. 상호 작용을 많이 할 필요가 없었습니다.)

조롱 부작용

부록 : 실제로, 시간이 걸린 부작용을 시뮬레이션해야했습니다. 그래서 잠을 자려면 객체의 방법이 필요했습니다. 다음과 같이 작동합니다.

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')

6

Aaron Hall의 답변이 저에게 효과적입니다. 한 가지 중요한 것을 언급하고 싶을뿐입니다.

에 만약 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

파이썬에서 수입품을 조롱하는 훌륭한 방법을 찾았습니다. 그건 에릭의 Zaadi의 솔루션을 찾을 여기 난 그냥 내 내부에서 사용하는 장고 응용 프로그램입니다.

나는 클래스있어 SeatInterface하는 인터페이스입니다 Seat모델 클래스를. 그래서 내 seat_interface모듈 안에는 그런 수입품이 있습니다.

from ..models import Seat

class SeatInterface(object):
    (...)

SeatInterfacemocked Seatclass as로 클래스에 대한 격리 된 테스트를 만들고 싶었습니다 FakeSeat. 문제는 장고 응용 프로그램이 다운 된 오프라인 실행 테스트 방법입니다. 나는 아래 오류가 있었다 :

잘못 구성됨 : BASE_DIR 설정을 요청했지만 설정이 구성되지 않았습니다. 설정에 액세스하기 전에 환경 변수 DJANGO_SETTINGS_MODULE을 정의하거나 settings.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.002 초에 1 번 테스트 수행

확인


3

당신이 그렇게하면 import ModuleB실제로 다음과 같이 내장 메소드 __import__를 호출합니다 .

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

__builtin__모듈 을 가져 와서 __builtin__.__import__메소드 주위에 랩퍼를 작성 하여이 메소드를 겹쳐 쓸 수 있습니다. 또는 모듈 의 NullImporter후크로 재생할 수 있습니다 imp. 예외를 except포착 하고 -block 에서 모듈 / 클래스를 비 웃으십시오.

관련 문서를 가리키는 포인터 :

docs.python.org : __import__

imp 모듈을 사용하여 내부 가져 오기 액세스

이게 도움이 되길 바란다. 더 많은 파이썬 프로그래밍 경계로 들어가서 a) 실제로 달성하고자하는 것을 확실하게 이해하고 b) 의미에 대한 철저한 이해가 중요하다는 것을 강력히 충고 하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.