2015-08-17 23 views
2

我是新手測試,需要一些幫助。Python 3 urlopen上下文管理器嘲諷

假定具有這種方法:

from urllib.request import urlopen 

def get_posts(): 
    with urlopen('some url here') as data: 
     return json.loads(data.read().decode('utf-8')) 

是如何測試此方法的問題(使用mock.patch裝飾如果可能的話)?

我現在擁有的一切:

@mock.patch('mymodule.urlopen') 
def test_get_post(self, mocked_urlopen): 
    mocked_urlopen.__enter__ = Mock(return_value=self.test_data) 
    mocked_urlopen.__exit__ = Mock(return_value=False) 
    ... 

但它似乎並不奏效。

P.S.有什麼方便的方式來處理數據變量(哪種類型是HTTPResponse)在測試中,所以它可能只是簡單的字符串?

+0

我不熟悉'mock'庫,但也許你可以檢查[unittest.mock](https://docs.python.org/3/library/unittest。 mock.html)文件的更多細節。 – Eric

+0

'self.test_data'必須是具有read()方法的對象,而不是字符串。 – jfs

回答

0

好吧,所以我寫了簡單的類來模擬上下文管理器。

class PatchContextManager: 

    def __init__(self, method, enter_return, exit_return=False): 
     self._patched = patch(method) 
     self._enter_return = enter_return 
     self._exit_return = exit_return 

    def __enter__(self): 
     res = self._patched.__enter__() 
     res.context = MagicMock() 
     res.context.__enter__.return_value = self._enter_return 
     res.context.__exit__.return_value = self._exit_return 
     res.return_value = res.context 
     return res 

    def __exit__(self, type, value, tb): 
     return self._patched.__exit__() 

用法:

with PatchContextManager('mymodule.method', 'return_string') as mocked: 
    a = mymodule.method(47) # a == 'return_string' 
    mocked.assert_called_with(47) 
    ... 
2

我用這個戰鬥爲好,終於想通了。 (Python 3語法):

import urllib.request 
import unittest 
from unittest.mock import patch, MagicMock 

class TestUrlopen(unittest.TestCase): 
    @patch('urllib.request.urlopen') 
    def test_cm(self, mock_urlopen): 
     cm = MagicMock() 
     cm.getcode.return_value = 200 
     cm.read.return_value = 'contents' 
     cm.__enter__.return_value = cm 
     mock_urlopen.return_value = cm 

     with urllib.request.urlopen('http://foo') as response: 
      self.assertEqual(response.getcode(), 200) 
      self.assertEqual(response.read(), 'contents') 

    @patch('urllib.request.urlopen') 
    def test_no_cm(self, mock_urlopen): 
     cm = MagicMock() 
     cm.getcode.return_value = 200 
     cm.read.return_value = 'contents' 
     mock_urlopen.return_value = cm 

     response = urllib.request.urlopen('http://foo') 
     self.assertEqual(response.getcode(), 200) 
     self.assertEqual(response.read(), 'contents') 
     response.close() 
+0

我可以問一下no_cm和cm有什麼區別嗎? – EralpB

+1

@EralpB cm意味着他顯示了一個使用urlopen作爲上下文管理器的示例,no_cm顯示了一個沒有將其用作上下文管理器的示例。 –