title: Python unit test failed because of StopIteration error from Mock
body: <p>I am testing a class object using mock which is ccxt.binance. This mocked object is passed as an argument to a function that I am testing. Initially it is working because I was able to test to up to a certain extent. However, when I changed a value of said object, the function is not respecting the value and it returns a StopIteration error. am I missing something with the mock set up? Here's a sample snippet of the code.</p>
  
  <p>Here is the code which is being tested</p>
  
  <pre class="lang-py prettyprint-override"><code>def exchange_history_load(args, ccxt_exchange):
      # skip exchange if has no true fetchOHLCV or if exchange has no timeframe specific data
      if ((not ccxt_exchange.has['fetchOHLCV']) or (ccxt_exchange.has['fetchOHLCV'] == 'emulated') or
         (not ccxt_exchange.timeframes.get(args.timeframe))):
          logging.warning(f'Skipping {ccxt_exchange.name}.')
          return
  
      # check if exchange is in database. Write to database if not.
      exchange_db_data = get_exchange(ccxt_exchange.id)
      if not exchange_db_data:
          insert_exchange_to_db(ccxt_exchange.id, ccxt_exchange.name)
          logging.info(f'Inserted {ccxt_exchange.name} to database.')
          exchange_db_data = get_exchange(ccxt_exchange.id)
      exchange_db_id = exchange_db_data[0]
      logging.info(f'Fetched {ccxt_exchange.name} data.')
  
      # load exchange markets
      markets = ccxt_exchange.load_markets()
      logging.info(f'Loaded {ccxt_exchange.name} markets.')
  
      return exchange_db_id, markets
  </code></pre>
  
  <p>Here is the test function</p>
  
  <pre class="lang-py prettyprint-override"><code>@patch('src.cron.historical_price.hp_load_data.insert_exchange_to_db')
  @patch('src.cron.historical_price.hp_load_data.get_exchange')
  @patch('ccxt.binance')
  def test_exchange_history_load(mock_binance, mock_get_exchange, mock_insert_exchange_to_db):
      # create sample arguments for function
      args = argparse.Namespace(timeframe='1d')
  
      # mock up sample return values for necessary functions
      mock_get_exchange.side_effect = [None, (1, 'binance', 'Binance'), (1, 'binance', 'Binance')]
      mock_binance.return_value.has.return_value = {'fetchOHLCV': True}
      mock_binance.return_value.load_markets.return_value = {
          'BTC/USDT': {
              'id': 'btcusdt',
              'symbol': 'BTC/USDT',
              'base': 'BTC',
              'quote': 'USDT',
              'active': True
          }
      }
      mock_binance.return_value.id.return_value = 'binance'
      mock_binance.return_value.name.return_value = 'Binance'
  
      ccxt_exchange = ccxt.binance()
  
      # case 1: exchange has fetchOHLCV, timeframe, but is not in database
  
      # test assertions with case 1
      exchange_db_id, markets = exchange_history_load(args, ccxt_exchange)
      assert type(exchange_db_id) == int
      assert type(markets) == dict
      assert mock_get_exchange.call_count == 2
      calls = [call(mock_binance.return_value.id), call(mock_binance.return_value.id)]
      mock_get_exchange.assert_has_calls(calls)
      mock_insert_exchange_to_db.assert_called_once()
      mock_binance.return_value.load_markets.assert_called_once()
  
      # case 2: exchange has fetchOHLCV, timeframe, and is in database
  
      # reset mock calls for case 2
      mock_insert_exchange_to_db.reset_mock()
      mock_get_exchange.reset_mock()
      mock_binance.return_value.load_markets.reset_mock()
  
      # test assertions with case 2
      exchange_db_id, markets = exchange_history_load(args, ccxt_exchange)
      assert type(exchange_db_id) == int
      assert type(markets) == dict
      mock_get_exchange.assert_called_once()
      assert not mock_insert_exchange_to_db.called
      mock_binance.return_value.load_markets.assert_called_once()
      calls = [call(mock_binance.return_value.id)]
      mock_get_exchange.assert_has_calls(calls)
  
      # case 3: exchange doesn't have true fetchOHLCV
  
  
      # reset_mock calls for case 3
      mock_get_exchange.reset_mock()
      mock_insert_exchange_to_db.reset_mock()
      mock_binance.reset_mock()
      mock_binance.return_value.has.return_value = {'fetchOHLCV': True}
      # mock_binance.return_value.load_markets.reset_mock()
  
      # test assertions with case 3
      exchange_db_id, markets = exchange_history_load(args, ccxt_exchange)
      # assert not exchange_db_id
      # assert not markets
      assert not mock_get_exchange.called
      assert not mock_insert_exchange_to_db.called
      assert not mock_binance.return_value.load_markets.called
  
  </code></pre>
  
  <p>Case 1 and case 2 works. But with case 3, an error is thrown.</p>
  
  <pre><code>mock_binance = &lt;MagicMock name='binance' id='140285596974272'&gt;
  mock_get_exchange = &lt;MagicMock name='get_exchange' id='140285596584312'&gt;
  mock_insert_exchange_to_db = &lt;MagicMock name='insert_exchange_to_db' id='140285596584480'&gt;
  
      @patch('src.cron.historical_price.hp_load_data.insert_exchange_to_db')
      @patch('src.cron.historical_price.hp_load_data.get_exchange')
      @patch('ccxt.binance')
      def test_exchange_history_load(mock_binance, mock_get_exchange, mock_insert_exchange_to_db):
          # create sample arguments for function
          args = argparse.Namespace(timeframe='1d')
  
          # mock up sample return values for necessary functions
          mock_get_exchange.side_effect = [None, (1, 'binance', 'Binance'), (1, 'binance', 'Binance')]
          mock_binance.return_value.has.return_value = {'fetchOHLCV': True}
          mock_binance.return_value.load_markets.return_value = {
              'BTC/USDT': {
                  'id': 'btcusdt',
                  'symbol': 'BTC/USDT',
                  'base': 'BTC',
                  'quote': 'USDT',
                  'active': True
              }
          }
          mock_binance.return_value.id.return_value = 'binance'
          mock_binance.return_value.name.return_value = 'Binance'
  
          ccxt_exchange = ccxt.binance()
  
          # case 1: exchange is not in database
  
          # test assertions with case 1
          exchange_db_id, markets = exchange_history_load(args, ccxt_exchange)
          assert type(exchange_db_id) == int
          assert type(markets) == dict
          assert mock_get_exchange.call_count == 2
          calls = [call(mock_binance.return_value.id), call(mock_binance.return_value.id)]
          mock_get_exchange.assert_has_calls(calls)
          mock_insert_exchange_to_db.assert_called_once()
          mock_binance.return_value.load_markets.assert_called_once()
  
          # case 2: exchange is in database
  
          # reset mock calls for case 2
          mock_insert_exchange_to_db.reset_mock()
          mock_get_exchange.reset_mock()
          mock_binance.return_value.load_markets.reset_mock()
  
          # test assertions with case 2
          exchange_db_id, markets = exchange_history_load(args, ccxt_exchange)
          assert type(exchange_db_id) == int
          assert type(markets) == dict
          mock_get_exchange.assert_called_once()
          assert not mock_insert_exchange_to_db.called
          mock_binance.return_value.load_markets.assert_called_once()
          calls = [call(mock_binance.return_value.id)]
          mock_get_exchange.assert_has_calls(calls)
  
          # case 3: exchange doesn't have true fetchOHLCV
  
          # reset_mock calls for case 3
          mock_get_exchange.reset_mock()
          mock_insert_exchange_to_db.reset_mock()
          mock_binance.reset_mock()
          mock_binance.return_value.has.return_value = {'fetchOHLCV': True}
          # mock_binance.return_value.load_markets.reset_mock()
  
          # test assertions with case 3
  &gt;       exchange_db_id, markets = exchange_history_load(args, ccxt_exchange)
  
  test_cron/test_historical_price/test_hp_load_data.py:89: 
  _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
  ../src/cron/historical_price/hp_load_data.py:75: in exchange_history_load
      exchange_db_data = get_exchange(ccxt_exchange.id)
  /usr/lib/python3.6/unittest/mock.py:939: in __call__
      return _mock_self._mock_call(*args, **kwargs)
  _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
  
  _mock_self = &lt;MagicMock name='get_exchange' id='140285596584312'&gt;
  args = (&lt;MagicMock name='binance().id' id='140285596659328'&gt;,), kwargs = {}
  self = &lt;MagicMock name='get_exchange' id='140285596584312'&gt;, _new_name = ''
  _new_parent = None
  _call = call(&lt;MagicMock name='binance().id' id='140285596659328'&gt;), seen = set()
  skip_next_dot = False, do_method_calls = False, name = 'get_exchange'
  
      def _mock_call(_mock_self, *args, **kwargs):
          self = _mock_self
          self.called = True
          self.call_count += 1
          _new_name = self._mock_new_name
          _new_parent = self._mock_new_parent
  
          _call = _Call((args, kwargs), two=True)
          self.call_args = _call
          self.call_args_list.append(_call)
          self.mock_calls.append(_Call(('', args, kwargs)))
  
          seen = set()
          skip_next_dot = _new_name == '()'
          do_method_calls = self._mock_parent is not None
          name = self._mock_name
          while _new_parent is not None:
              this_mock_call = _Call((_new_name, args, kwargs))
              if _new_parent._mock_new_name:
                  dot = '.'
                  if skip_next_dot:
                      dot = ''
  
                  skip_next_dot = False
                  if _new_parent._mock_new_name == '()':
                      skip_next_dot = True
  
                  _new_name = _new_parent._mock_new_name + dot + _new_name
  
              if do_method_calls:
                  if _new_name == name:
                      this_method_call = this_mock_call
                  else:
                      this_method_call = _Call((name, args, kwargs))
                  _new_parent.method_calls.append(this_method_call)
  
                  do_method_calls = _new_parent._mock_parent is not None
                  if do_method_calls:
                      name = _new_parent._mock_name + '.' + name
  
              _new_parent.mock_calls.append(this_mock_call)
              _new_parent = _new_parent._mock_new_parent
  
              # use ids here so as not to call __hash__ on the mocks
              _new_parent_id = id(_new_parent)
              if _new_parent_id in seen:
                  break
              seen.add(_new_parent_id)
  
          ret_val = DEFAULT
          effect = self.side_effect
          if effect is not None:
              if _is_exception(effect):
                  raise effect
  
              if not _callable(effect):
  &gt;               result = next(effect)
  E               StopIteration
  
  /usr/lib/python3.6/unittest/mock.py:998: StopIteration
  </code></pre>