5

クラス レベル (インスタンスではない) の属性を初期化するために使用されるモジュール レベルの関数をモックしたいと思います。簡単な例を次に示します。

# a.py    
def fn(): 
    return 'asdf'

class C:
    cls_var = fn()

a.fn() をモックしようとする単体テストを次に示します。

# test_a.py 
import unittest, mock
import a

class TestStuff(unittest.TestCase):
    # we want to mock a.fn so that the class variable
    # C.cls_var gets assigned the output of our mock

    @mock.patch('a.fn', return_value='1234')
    def test_mock_fn(self, mocked_fn):
        print mocked_fn(), " -- as expected, prints '1234'"
        self.assertEqual('1234', a.C.cls_var) # fails! C.cls_var is 'asdf'

問題はどこにパッチを当てるかだと思いますが、インポートで両方のバリエーションを試しましたが、うまくいきませんでした。aC がスコープに入る前にモックされた a.fn() が「存在」するように、インポートステートメントを test_mock_fn() に移動しようとしました-いいえ、まだ失敗します。

どんな洞察も大歓迎です!

4

2 に答える 2

5

ここで実際に起こっていることは、実際にモジュールをインポートすると、fn()すでに実行されているということです。そのため、クラス属性に格納されているメソッドを既に評価した後に、モックが入ります。

そのため、メソッドをモックしようとするときには、実行しようとしているテストには遅すぎます。

メソッドに print ステートメントを追加するだけでも、これが起こっていることがわかります。

def fn():
    print("I have run")
    return "asdf"

テストモジュールで、テストを実行せずにインポートaして単純に実行するとI have run、モジュールから明示的に何も実行せずにコンソール出力に表示されることがわかりaます。

したがって、ここで取ることができる 2 つのアプローチがあります。PropertyMockを使用して、次のように、クラス属性を保存する予定のものにモックアウトできます。

@mock.patch('a.C.cls_var', new_callable=PropertyMock)
def test_mock_fn(self, mocked_p):
    mocked_p.return_value = '1234'

    self.assertEqual('1234', a.C.cls_var)

さて、これを行うことによって、まだ実際には を実行していることにも注意する必要がありますがfn、この嘲笑により、設定した で '1234' を保持しcls_varていることになりPropertyMockます。

次の提案 (設計の変更が必要になるため、おそらくあまり理想的ではありません) では、class 属性を使用している理由を修正する必要があります。実際にそのクラス属性をインスタンス属性として設定すると、インスタンスを作成するときにCメソッドが実行され、その時点でモックが使用されるためです。

したがって、クラスは次のようになります。

class C:
    def __init__(self):
        self.var = fn()

テストは次のようになります。

@mock.patch('a.fn', return_value='1234')
def test_mock_fn(self, mocked_p):
    self.assertEqual('1234', a.C().var)
于 2016-02-21T02:27:01.863 に答える