Patching a method without changing how the method works?

佐手、 提交于 2019-12-13 02:12:20

问题


I'm trying to test that a pandas method gets called with some values.

However, just by applying a @patch decorator causes the patched method to throw a ValueError within pandas, when the actual method does not. I'm just trying to test that Stock.calc_sma is calling the underlying pandas.rolling_mean function.

I'm under the assumption that the @patch decorator basically adds some "magic" methods to the thing I'm patching that allow me to check if the function was called. If this is the case, why doesn't the pandas.rolling_mean function behave the same whether it's patched vs. not patched?

app/models.py

import pandas as pd
class Stock:  # i've excluded a bunch of class methods, including the one that sets self.data, which is a DataFrame of stock prices.
    def calc_sma(self, num_days)
        if self.data.shape[0] > num_days:  # Stock.data holds a DataFrame of stock prices
                column_title = 'sma' + str(num_days)
                self.data[column_title] = pd.rolling_mean(self.data['Adj Close'], num_days)

app/tests/TestStockModel.py

def setUp(self):
    self.stock = MagicMock(Stock)
    self.stock.ticker = "AAPL"
    self.stock.data = DataFrame(aapl_test_data.data)

@patch('app.models.pd.rolling_mean')
def test_calc_sma(self, patched_rolling_mean):
    Stock.calc_sma(self.stock, 3)
    assert(isinstance(self.stock.data['sma3'], Series))
    patched_rolling_mean.assert_any_call()

ERROR: test_calc_sma (TestStockModel.TestStockModel)

Traceback (most recent call last):
  File "/Users/grant/Code/python/chartflux/env/lib/python2.7/site-packages/mock.py", line 1201, in patched
    return func(*args, **keywargs)
  File "/Users/grant/Code/python/chartflux/app/tests/TestStockModel.py", line 26, in test_calc_sma
    Stock.calc_sma(self.stock, 3)
  File "/Users/grant/Code/python/chartflux/app/models.py", line 27, in calc_sma
    self.data[column_title] = pd.rolling_mean(self.data['Adj Close'], num_days)
  File "/Users/grant/Code/python/chartflux/env/lib/python2.7/site-packages/pandas/core/frame.py", line 1887, in __setitem__
    self._set_item(key, value)
  File "/Users/grant/Code/python/chartflux/env/lib/python2.7/site-packages/pandas/core/frame.py", line 1967, in _set_item
    value = self._sanitize_column(key, value)
  File "/Users/grant/Code/python/chartflux/env/lib/python2.7/site-packages/pandas/core/frame.py", line 2017, in _sanitize_column
    raise ValueError('Length of values does not match length of '
ValueError: Length of values does not match length of index

回答1:


>>> import os
>>> os.getcwd()
'/'
>>> from unittest.mock import patch
>>> with patch('os.getcwd'):
...     print(os.getcwd)
...     print(os.getcwd())
...     print(len(os.getcwd()))
...
<MagicMock name='getcwd' id='4472112296'>
<MagicMock name='getcwd()' id='4472136928'>
0

By default patch replaces things with really generic mock objects. As you can see, calling the mock just returns another mock. It has a len of 0 even if the replaced object wouldn't have a len. Its attributes are also generic mocks.

So to simulate behavior requires things extra arguments like:

>>> with patch('os.getcwd', return_value='/a/wonderful/place'):
...     os.getcwd()
...
'/a/wonderful/place'

Or to "pass through":

>>> _cwd = os.getcwd
>>> with patch('os.getcwd') as p:
...     p.side_effect = lambda: _cwd()
...     print(os.getcwd())
...
/

There is a similar example in https://docs.python.org/3.5/library/unittest.mock-examples.html



来源:https://stackoverflow.com/questions/25097061/patching-a-method-without-changing-how-the-method-works

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!