Search code examples
pythonunit-testingmockingnose

Response generated while mocking is not correct in python tests


I am trying to unit test some of my functions using mocks in python. Few of them seems to be working as expected but I am trouble with the response of one the test case. Which it not returning the ideal response which it should.

###############motpenrichreceiverinfo.py##################

import requests

def getReceiverInfo(fundId):
    print('Inside fetchReceiverInfo',fundId)
    response = requests.get("REST URL FOR RECEIVER INFO")
    if response.ok:
        return response
    else:
        return None
    
def getMotpTrade(trade_id):
    response = requests.get("REST URL")
    if response.ok:
        return response
    else:
        return None

def getMotpCanonical(trade_id):
    response = requests.get("REST URL")
    if response.ok:
        return response
    else:
        return None
    
def EnrichTradeWithReceiverInfo(event, context):
    #print('Lambda function started..')
    trade_id = event['tradeId']
    motpTrade = getMotpTrade(trade_id)
    canonicalTrade = getMotpCanonical(trade_id)

    fundId = motpTrade['Account']
    #print(fundId)
    data = getReceiverInfo(fundId)
    print(data)
    return data

##########################test_motptrade.py##############################

# Standard library imports
from unittest.mock import Mock, patch

# Third-party imports...
from nose.tools import assert_true
from nose.tools import assert_is_not_none, assert_list_equal, assert_dict_equal

# Local imports
from motpenrichreceiverinfo import getMotpTrade, getReceiverInfo, EnrichTradeWithReceiverInfo

@patch('motpenrichreceiverinfo.requests.get')
def test_motptraderequest_response(mock_get):
    motpTrade = [{
            "motpTrade":"motpTrade"
    }]

    # Configure the mock to return a response with an OK status code.
    mock_get.return_value.ok = True
    mock_get.return_value.json.return_value = motpTrade
    
    # Send a request to the API server and store the response.
    response = getMotpTrade('tradeId')

    # If the request is sent successfully, then I expect a response to be returned.
    assert_list_equal(response.json(), motpTrade)

@patch('motpenrichreceiverinfo.requests.get')
def test_getReceiverInfo_respose_ok(mock_get):
    receiverInfo = [{
        "reciever":"reciever"
    }]

    # Configure the mock to return a response with an OK status code.
    mock_get.return_value.ok = True
    mock_get.return_value.json.return_value = receiverInfo
    
    # Send a request to the API server and store the response.
    response = getReceiverInfo("1110")

    # If the request is sent successfully, then I expect a response to be returned.
    assert_list_equal(response.json(), receiverInfo)

@patch('motpenrichreceiverinfo.getMotpTrade')
@patch('motpenrichreceiverinfo.getMotpCanonical')
@patch('motpenrichreceiverinfo.getReceiverInfo')
def test_EnrichTradeWithReceiverInfo_ok(mock_get,mock_canonical,mock_receiverinfo):
    motpTrade = [{
            "motpTrade":"motpTrade"
    }]
    
    receiverInfo = [{
            "reciever":"reciever"
        }]
    event = {"tradeId":"123456"}
    
    # Configure the mock to return a response with an OK status code.
    mock_get.return_value = Mock(ok=True)
    mock_get.return_value.json.return_value = motpTrade

    mock_canonical.return_value.ok = True
    mock_canonical.return_value.json.return_value = [{}]

    mock_receiverinfo.return_value.ok = True
    mock_receiverinfo.return_value.json.return_value = receiverInfo
    
    data = EnrichTradeWithReceiverInfo(event,"")
    assert_true(mock_get.called)
    assert_true(mock_receiverinfo.called)
    assert_list_equal(data.json(),receiverInfo) 

Here my first two cases are working as expected. But my last test case(test_EnrichTradeWithReceiverInfo_ok) is somehow not working. Ideally it should have response equal to receiverInfo object.But when I run it gives below error. It would be really helpful someone can help me understand what I am doing wrong here.

(venv) C:\Development\motplambdas>nosetests -v test_motptrade.py
test_motptrade.test_EnrichTradeWithReceiverInfo_ok ... FAIL
test_motptrade.test_getReceiverInfo_respose_ok ... ok
test_motptrade.test_motptraderequest_response ... ok

======================================================================
FAIL: test_motptrade.test_EnrichTradeWithReceiverInfo_ok
----------------------------------------------------------------------
Traceback (most recent call last):
  File "c:\development\motplambdas\venv\lib\site-packages\nose\case.py", line 198, in runTest
    self.test(*self.arg)
  File "c:\program files\python38\lib\unittest\mock.py", line 1325, in patched
    return func(*newargs, **newkeywargs)
  File "C:\Development\motplambdas\test_motptrade.py", line 69, in test_EnrichTradeWithReceiverInfo_ok
    assert_list_equal(data.json(),receiverInfo)
AssertionError: Lists differ: [{'motpTrade': 'motpTrade'}] != [{'reciever': 'reciever'}]

First differing element 0:
{'motpTrade': 'motpTrade'}
{'reciever': 'reciever'}

- [{'motpTrade': 'motpTrade'}]
+ [{'reciever': 'reciever'}]

----------------------------------------------------------------------
Ran 3 tests in 0.011s

FAILED (failures=1)

Solution

  • The issue is the order of which you are passing your Mock objects into the function call.

    @patch('motpenrichreceiverinfo.getMotpTrade')
    @patch('motpenrichreceiverinfo.getMotpCanonical')
    @patch('motpenrichreceiverinfo.getReceiverInfo')
    def test_EnrichTradeWithReceiverInfo_ok(mock_get,mock_canonical,mock_receiverinfo)
    

    Mock objects are passed from the bottom up, meaning @patch('motpenrichreceiverinfo.getReceiverInfo') is the first Mock passed in to the function call, not the last as you have it listed. Due to this you end up setting getReceiverInfo to return the wrong value. The solution is to switch the function call to look like this:

    def test_EnrichTradeWithReceiverInfo_ok(mock_receiverinfo, mock_canonical, mock_get)
    

    You can read more about this here where it explains how nesting decorators works.

    Note that the decorators are applied from the bottom upwards. This is the standard way that Python applies decorators. The order of the created mocks passed into your test function matches this order.