2017-10-23 11 views
-2

私は、文字列のリストから文字列をランダムに選択するクラスメソッドに対してpytestを使ってテストを開発しようとしています。それは基本的に以下のgivemeanumber方法のように見えますpytestでmonkeypatchingを働かせること

第一の方法、getsshは、クラスでボブがpytest docs

からわずか一例である私の生産コードはから文字列のリストを取得しDBを選択してランダムに選択します。だから私は私のテスト文字列を取得し、ランダムに選択する代わりに、最初の文字列を選択したいと思います。そうすれば、私は既知の文字列に対してテストすることができます。

私の読書から、私はランダム化を偽造するためにモンキーパッチを使用する必要があると思う。ここで

は、私がこれまで持っているものだ

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前に'輸入random'を試してみましたか? –

+0

'import random.choice'を' from random import choice'の代わりに使用し、それに応じてコードを調整してください。しかし、私はむしろ依存性注入を使い、猿のパッチを避けるでしょう。 – Goyo

+0

この構文はモジュールのためのものであり、 'choice'は関数であるため、' random.choice'をインポートすることはできません。それはちょうど失敗するでしょう。 –

答えて

0

この問題は、変数名がPythonでどのように処理されるかに起因しています。他の言語との主な相違点は、変数の値の名前による指定がないことです。変数名のオブジェクトへの結合のみが存在します。

これは、この質問の範囲外の大きなテーマであるが、以下のような結果がある:

  1. あなたはモジュールrandomから機能choiceをインポートする場合、あなたはその関数に名前choiceをバインドインポート時にそこに存在し、この名前をbobモジュールのローカル名前空間に配置します。

  2. random.choiceにパッチを当てると、モジュールrandomchoiceという名前が新しいモックオブジェクトに再バインドされます。

  3. ただし、bobモジュールに既にインポートされている名前は、依然として元の機能を参照しています。誰もそれにパッチを当てなかったからです。関数自体は変更されておらず、名前が置き換えられただけです。

  4. したがって、Bobクラスは元のrandom.choice関数を呼び出します。模倣された関数ではありません。

この問題を解決するために(彼らは矛盾しているように、両方ではない)、次の2つの方法のいずれかに従うことができます:


A:常にその正確なフルネームでrandom.choice()関数を呼び出す(つまり、いいえ。choice)。そして、もちろん、import randomfrom random import ...ない)の前に - あなたがos.path.expanduser()のために行うのと同じ。

# 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 

B:その場合は(ないrandom.choice())でbob.choice()であるあなたが呼び出す実際の機能を、パッチを適用します。

# 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に持っている - すなわち、パッチが適用されているモジュールに名前randomをバインドします。

ちょうどfrom random import choiceを実行すると、という名前をバインドしますが、変数のローカル名前空間にはrandomをバインドしません。

関連する問題