2017-07-18 69 views
1

我有一種情況,就是類似於這樣:如何使用requests_mock來確保調用正確的會話?

import unittest 
import requests_mock 
import requests 

import mock 

class ClassUnderTest: 
    def __init__(self): 
     self.session = requests.Session() 
     self.foo_url = 'http://google.com' 

    def do_foo(self): 
     return self.session.get(self.foo_url) 

    def do_foo_failing(self): 
     # I want to prevent accidentally doing this sort of operation 
     # instead of reusuing self.session 
     return requests.get(self.foo_url) 

我想,以確保該方法正確執行,也保證了我使用的self.session對象。在實踐中,我遇到了這個問題,因爲我錯誤地添加了一個requests.get而不是重新使用同一個會話。

然而,這意味着這樣的測試不實際測試這一功能:

class TestClassUnderTest(unittest.TestCase): 

    @requests_mock.mock() 
    def test_do_foo_should_pass(self, m): 
     c = ClassUnderTest() 

     m.get('http://google.com', status_code=200) 
     r = c.do_foo() 

     self.assertEqual(200, r.status_code) 
     self.assertEqual(m.call_count, 1) 

    @requests_mock.mock() 
    def test_do_foo_should_fail(self, m): 
     c = ClassUnderTest() 
     m.get('http://google.com', status_code=200) 
     r = c.do_foo_failing() 

     self.assertEqual(200, r.status_code) 
     self.assertEqual(m.call_count, 1) 

我曾經想過用模擬替代self.session,但後來我也有做的事情,比如集合STATUS_CODE的模擬(和在我的真實代碼示例中,我還需要執行諸如添加模擬.json() method以便正確使用響應的代碼功能)。

有沒有一種方法可以使用requests_mock來有效地保證只有self.session被使用(在本例中不是requests.get)?

回答

0

以下作品包裝所有的request.Session功能的MockClass,以跟蹤他們的使用的包裝:

class MockSession: 
    def __init__(self): 
     self.session = requests.Session() 
     self._called_methods = {} 

     def session_decorator(f): 
      def func(*args, **kwargs): 
       if f.__name__ not in self._called_methods: 
        self._called_methods[f.__name__] = 1 
       else: 
        self._called_methods[f.__name__] += 1 
       return f(*args, **kwargs) 
      return func 

     for name, f in inspect.getmembers(self.session): 
      if inspect.ismethod(f): 
      setattr(self, name, session_decorator(f)) 

使用這種模擬可以訪問._called_methods值後,以檢查多久單個方法叫做。請注意,由於requests_mock的工作原理,您必須在運行時執行此操作(而不是通過簡單地擴展具有類似功能的requests.Session類的加載時間)。

修改測試代碼的結果:

class TestClassUnderTest(unittest.TestCase): 

    @requests_mock.mock() 
    def test_do_foo_should_pass(self, m): 
     c = ClassUnderTest() 
     c.session = MockSession() 
     m.get('http://google.com', status_code=200) 
     r = c.do_foo() 

     self.assertEqual(200, r.status_code) 
     self.assertEqual(m.call_count, 1) 
     self.assertEqual(c.session._called_count['GET'], 1) 

    @requests_mock.mock() 
    def test_do_foo_should_fail(self, m): 
     c = ClassUnderTest() 
     c.session = MockSession() 
     m.get('http://google.com', status_code=200) 
     r = c.do_foo_failing() 

     self.assertEqual(200, r.status_code) 
     self.assertEqual(m.call_count, 1) 

     # This will fail with an attribute error 
     # because that function was never invoked on the mock session 
     self.assertEqual(c.session._called_count['GET'], 1)