2017-10-23 2 views
-2

나는 문자열 목록에서 임의로 문자열을 선택하는 클래스 메소드에 대해 pytest를 사용하여 테스트를 개발하려고합니다.pytest에서 작동하는 monkeypatching 받기

그것은 아래 givemeanumber 방법과 같은 본질적으로 같습니다

import os.path 
from random import choice 

class Bob(object): 
    def getssh(): 
    return os.path.join(os.path.expanduser("~admin"), '.ssh') 

    def givemeanumber(): 
    nos = [1, 2, 3, 4] 
    chosen = choice(nos) 
    return chosen 

첫 번째 방법, getssh은 수업 시간에 밥은 내 생산 코드에서 문자열 목록을 페치 pytest docs

에서 그냥 예입니다 DB를 선택한 다음 임의로 하나를 선택합니다. 그래서 내 테스트 문자열을 가져오고 무작위로 선택하는 대신 첫 번째 문자열을 선택합니다. 그렇게하면 알려진 문자열에 대해 테스트 할 수 있습니다.

내 독서에서 나는 무작위 배치를 위조하기 위해 monkeypatching을 사용해야한다고 생각합니다.

는 여기에 지금까지

import os.path 
from random import choice 
from _pytest.monkeypatch import MonkeyPatch 
from bob import Bob 

class Testbob(object): 
    monkeypatch = MonkeyPatch() 

    def test_getssh(self): 
     def mockreturn(path): 
      return '/abc' 
     Testbob.monkeypatch.setattr(os.path, 'expanduser', mockreturn) 
     x = Bob.getssh() 
     assert x == '/abc/.ssh' 

    def test_givemeanumber(self): 
     Testbob.monkeypatch.setattr('random.choice', lambda x: x[0]) 
     z = Bob.givemeanumber() 
     assert z == 1 

있어 첫 번째 시험 방법은 다시 (내가 테스트 클래스에서 사용하고 같은 약간 적응)이 pytest 워드 프로세서의 예입니다거야. 이것은 잘 작동합니다.

내가 Testbob.monkeypatch.setattr(random, 'choice', lambda x: x[0]) 를 사용하는 기대할 수있는 문서의 예에 따라

하지만 Testbob.monkeypatch.setattr('random.choice', lambda x: x[0])

이 더 얻을로 변경하지만 스와핑하는 것은 발생하지 않는 경우이 NameError: name 'random' is not defined

을 산출 : AssertionError: assert 2 == 1

작업에 적합한 도구를 monkeypatching합니까? 내가 잘못 가고 있다면 어디로 가고 있습니까?

+0

'os.path'에서와 같이 monkeypatching하기 전에'import random'을 시도해 보셨습니까? –

+0

'from random import choice' 대신'import random.choice'를 사용하고 이에 따라 코드를 조정하십시오. 하지만 나는 의존성 주입을 사용하고 원숭이 패치를 피하고 싶습니다. – Goyo

+0

이 구문은 모듈만을위한 것이므로'random.choice'를 임포트 할 수 없습니다.'choice'는 함수입니다. 그것은 단지 실패 할 것이다. –

답변

0

문제는 변수 이름이 파이썬에서 어떻게 처리되는지에 기인합니다. 다른 언어와의 주요 차이점은 변수 이름에 대한 값의 할당이 없다는 것입니다. 개체에 변수 이름의 바인딩 만 있습니다.

이이 질문의 범위를 벗어난 더 큰 주제이지만, 다음과 같이 결과는 다음과 같습니다 당신이 모듈 random에서 함수 choice을 가져올 때

  1. , 당신은 함수에 이름 choice을 결합하는 가져 오기 순간에 존재하며이 이름을 bob 모듈의 로컬 네임 스페이스에 배치하십시오.

  2. 패치 random.choice을 패치 할 때 모듈 randomchoice 이름을 새 mock 객체에 다시 바인드합니다.

  3. 그러나 bob 모듈에 이미 가져온 이름은 여전히 ​​원래 기능을 나타냅니다. 아무도 그것을 패치하지 않았기 때문에. 함수 자체가 수정되지 않았으며 이름 만 바뀌 었습니다.

  4. 따라서 Bob 클래스는 조롱 된 것이 아니라 원래 random.choice 함수를 호출합니다.(그들은 충돌 한,하지만 모두)

이 문제를 해결하려면 두 가지 방법 중 하나를 수행하십시오


A는 : 항상 그 정확한 이름으로 random.choice() 함수를 호출 (즉, not choice). 물론 import random 전에 (from random import ...이 아님) - os.path.expanduser()과 동일합니다.


B

# bob.py 
import os.path 
import random 

class Bob(object): 
    @classmethod 
    def getssh(cls): 
    return os.path.join(os.path.expanduser("~admin"), '.ssh') 

    @classmethod 
    def givemeanumber(cls): 
    nos = [1, 2, 3, 4] 
    chosen = random.choice(nos) # <== !!! NOTE HERE !!!! 
    return chosen 

:이 경우 (하지 random.choice())에 bob.choice()을 당신이 전화 실제 기능을 패치. 알 수없는 이름 random하여 원래 오류에 대해서는

# test.py 
import os.path 
from _pytest.monkeypatch import MonkeyPatch 
from bob import Bob 

class Testbob(object): 
    monkeypatch = MonkeyPatch() 

    def test_givemeanumber(self): 
     Testbob.monkeypatch.setattr('bob.choice', lambda x: x[0]) 
     z = Bob.givemeanumber() 
     assert z == 1 

는 : - 즉 패치되고있는 모듈에 이름 random을 결합하면 patch(random, 'choice', ...)에 watn 경우에, 당신은 import random에 있습니다.

단지 from random import choice 일 경우, choice이라는 이름을 바인딩하지만 random은 변수의 로컬 네임 스페이스에 바인딩하지 않습니다.

관련 문제