Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python mocking: How to test the number of calls on a recursive function?

I have a recursive function living in a module called test_module

import requests    

def send_msg(msg, retries=0):
    try:
        # send the message here, e.g. a http request
        response = requests.get("http://www.doesnotexist98734.com")
        # if url does not exist raise an exception
    except Exception as e:
        if retries == 0:
            raise e
        else:
            return send_msg(msg, retries=retries-1)

My question is how can I write a unittest that checks the send_msg function is called n times when I set retries = n. I was playing around with the mock module (I'm using python 2.7) and I think I want something a bit like this,

import mock, unittest

class MyUnitTest(unittest.TestCase):

    @mock.patch('test_module.send_msg')
    def test_send_msg_tries_n_times(self, mock_send_msg):
        with self.assertRaises(Exception):
            mock_send_msg("hello", retries=3)
        self.assertEqual(mock_send_msg.call_count, 4) # initial call + 3 retries

However since I have mocked the function it won't call the real function so I don't get an exception nor does it call itself recursively...

like image 751
scruffyDog Avatar asked Sep 01 '25 01:09

scruffyDog


1 Answers

You can't mock the function under test. You want to test for expected results, not if the function used recursion correctly.

Mock the request.get() call, and have it always produce an exception. Then count how often your mock was called.

@mock.patch('requests.get')
def test_send_msg_tries_n_times(self, req_get_mock):
    req_get_mock.side_effect = Exception
    with self.assertRaises(Exception):
        send_msg("hello", retries=3)
    self.assertEqual(req_get_mock.call_count, 4)  # 1 initial call + 3 retries

If in future you want to avoid using recursion and want to use iteration instead, your test still will work as it validates the behaviour, not the specific implementation. You can safely refactor the function-under-test.

like image 101
Martijn Pieters Avatar answered Sep 02 '25 14:09

Martijn Pieters