abhi09
abhi09

Reputation: 81

Python - Monkey Patching weird bug

My Fake Mock Class looks like following:

class FakeResponse:
    method = None               # 
    url = None                  # static class variables

    def __init__(self, method, url, data):#, response):
        self.status_code = 200       # always return 200 OK
        FakeResponse.method = method # 
        FakeResponse.url = url       #

    @staticmethod
    def check(method, url, values):
       """ checks method and URL.      
       """
       print "url fake: ", FakeResponse.url
       assert FakeResponse.method == method
       assert FakeResponse.url == url

I have another decorator which is applicable over all the test cases:

@pytest.fixture(autouse=True)
def no_requests(monkeypatch):
    monkeypatch.setattr('haas.cli.do_put',
                    lambda url,data: FakeResponse('PUT', url, data))
    monkeypatch.setattr("haas.cli.do_post",
                    lambda url,data: FakeResponse('POST', url, data))
    monkeypatch.setattr("haas.cli.do_delete",
                    lambda url: FakeResponse('DELETE', url, None))

I am using Py.test to test the code.

Some example test cases are:

class Test:
    #test case passes
    def test_node_connect_network(self):
       cli.node_connect_network('node-99','eth0','hammernet')
       FakeResponse.check('POST','http://abc:5000/node/node-99/nic/eth0/connect_network',
                        {'network':'hammernet'})

    # test case fails
    def test_port_register(self):
        cli.port_register('1') # This make a indirect REST call to the original API
        FakeResponse.check('PUT','http://abc:5000/port/1', None)

    # test case fails
    def test_port_delete(self):
        cli.port_delete('port', 1)
        FakeResponse.check('DELETE','http://abc:5000/port/1', None)  

A sample error message which I get:

    method = 'PUT', url = 'http://abc:5000/port/1', values = None

    @staticmethod
    def check(method, url, values):
        """ checks method and URL.
           'values': if None, verifies no data was sent.
           if list of (name,value) pairs, verifies that each pair is in 'values'
        """
        print "url fake: ", FakeResponse.url
>       assert FakeResponse.method == method
E       assert 'POST' == 'PUT'
E         - POST
E         + PUT
haas/tests/unit/cli_v1.py:54: AssertionError
--------------------------------------------- Captured stdout call -------------------------------------
port_register <port>
Register a <port> on a switch
url fake:  http://abc:5000/node/node-99/nic/eth0/connect_network
--------------------------------------------- Captured stderr call -------------------------------------
Wrong number of arguements.  Usage: 

Whereas if I call the second test case in the following way considering the check function takes "self" argument and @staticmethod is not used then the test case works:

def test_port_register(self):
    cli.port_register('1')
    fp = FakeResponse('PUT','http://abc:5000/port/1', None) #Create a FakeResponse class instance
    fp.check('PUT','http://abc:5000/port/1', None) # Just call the check function with the same                                                          
                                                     arguments

Questions:

Sorry for the long post, I have been trying to resolve this for a week and wanted some perspective of other programmers.

Upvotes: 0

Views: 461

Answers (1)

abhi09
abhi09

Reputation: 81

The issue was not having the right signature for one of the functions. It was resolved by changing the argument passed to the MonkeyPatch function as en empty dictionary {} instead of 'None' value which is kind of specific to my code.

The reason the I was initially hitting the issue was, as the current function's call(cli.port_register) was failing when the parameters where passed to port_register itself, so it picked up the argument values from a previous state and doing the assert with the FakeResponse call.

Upvotes: 2

Related Questions