2016-09-12 17 views
5

現在、複数のレイヤー/ネストされた戻り値をモックする良い方法を見つけるのに苦労しています。言い換えれば、私は魔法のモックを返したいと思っています。それは、それ自身のセット戻り値で魔法のモックを返します。私はこれを比較的厄介で、よりエレガントで維持しやすい解決策を探しています。Pythonでネスト/リターンオブジェクトの複数のレイヤーを模擬する方法

次のコードを効率的にテストしようとしています。すべて、最後に

class MyTestCase(TestCase): 
    @patch('load_json_path.urlopen') 
    def test_load_json(self, mock_urlopen): 
     ### trying to simplify all of this 
     # third nested return 
     mock_decode = MagicMock(return_value='["myjsondata"]') 
     # second nested return value 
     mock_response = MagicMock() 
     mock_response.read.return_value=mock_decode 
     # first nested return value 
     mock_urlopen.return_value = mock_response 
     ### trying to simplify all of this    

     load_json() 

import json 
from urllib.request import url open 

def load_json(): 
    # first return value 
    response = urlopen("http://someurl.com/api/getjson") 
    # in turn, contains two nested return values for read and decode 
     response_dict = json.loads(response.read().decode('utf-8')) 

は、これは私がメンテナンスが煩雑に非常に洗練された、これまでのところ、これを嘲笑して行いました方法です:URLは、さらなる処理を必要とするJSON文字列を返します。私は模擬しようとしているデコード関数から返されたデータは、URLのオープン関数から発生します。 の方法を使用して、これは1行またはより簡単な方法で行うことができます。 理想的にはモックがtest_load_json機能で次のようになります。

mock_urlopen.__enter__.loads.__enter__.decode.return_value = '["myjsondata"]' 

残念ながら、私はモックドキュメントに有用何かを見つけるように見えることはできません。どんな助けもありがたい。

答えて

9

これは容易に可能であり、文書化されています。しかし、名前は簡単ではなく、探しているものを知る必要があります。 mockingと呼ばれるのは連鎖呼び出しであり、実際にはmockライブラリに文書化されています。

この例では、mock_urlopenは次のようになります。

mock_urlopen.return_value.read.return_value.decode.return_value = '["myjsondata"]' 

これが見事に動作します。詳細はPythonのドキュメントをチェックアウトするために:https://docs.python.org/3/library/unittest.mock-examples.html#mocking-chained-calls

+0

まあ、私は「美しく」言わないだろう... –

1

私は、ヘルパークラスとしてあなたのためにこれを作った:

from unittest.mock import Mock 

class ProxyMock: 
    """Put me for easy referral""" 
    def __init__(self, mock, _first=True): 
     self._mock_ = mock 
     self._first_ = _first 

    def __getattr__(self, name): 
     if self._first_: 
      new_mock = getattr(self._mock_, name) 
     else: 
      new_mock = getattr(self._mock_.return_value, name) 
     return ProxyMock(new_mock, _first=False) 

    def __setattr__(self, name, value): 
     if name in ("_mock_", "_first_"): 
      return super().__setattr__(name, value) 
     setattr(self._mock_, name, value) 

a = Mock() 
ProxyMock(a).b.c.return_value = 123 

assert a.b().c() == 123 
関連する問題