Home > Mobile >  How to mock client object
How to mock client object

Time:12-23

I am working on writing unittest for my fastapi project.
One endpoint includes getting a serviceNow ticket. Here is the code i want to test:


from aiosnow.models.table.declared import IncidentModel as Incident
from fastapi import APIRouter

router = APIRouter()
@router.post("/get_ticket")
async def snow_get_ticket(req: DialogflowRequest):
    """Retrieves the status of the ticket in the parameter."""

    client = create_snow_client(
        SNOW_TEST_CONFIG.servicenow_url, SNOW_TEST_CONFIG.user, SNOW_TEST_CONFIG.pwd
    )

    params: dict = req.sessionInfo["parameters"]
    ticket_num = params["ticket_num"]

    try:
        async with Incident(client, table_name="incident") as incident:
            response = await incident.get_one(Incident.number == ticket_num)
            stage_value = response.data["state"].value
            desc = response.data["description"]

     [...data manipulation, unimportant parts]

What i am having trouble with is trying to mock the client response, every time the actual client gets invoked and it makes the API call which i dont want.
Here is the current version of my unittest:

from fastapi.testclient import TestClient
client = TestClient(app)

@patch("aiosnow.models.table.declared.IncidentModel")
    def test_get_ticket_endpoint_valid_ticket_num(self, mock_client):
        mock_client.return_value = {"data" : {"state": "new",
                                          "description": "test"}} 
        
        response = client.post(
            "/snow/get_ticket", json=json.load(self.test_request)
        )
        assert response.status_code == 200

I think my problem is patching the wrong object, but i am not sure what else to patch.

CodePudding user response:

In your test your calling client.post(...) if you don't want this to go to the Service Now API this client should be mocked.

Edit 1: Okay so the way your test is setup now the self arg is the mocked IncidentModel object. So only this object will be a mock. Since you are creating a brand new IncidentModel object in your post method it is a real IncidentModel object, hence why its actually calling the api. enter image description here

In order to mock the IncidentModel.get_one method so that it will return your mock value any time an object calls it you want to do something like this:

def test_get_ticket_endpoint_valid_ticket_num(mock_client):
    mock_client.return_value = {"data" : {"state": "new",
                                    "description": "test"}} 
    with patch.object(aiosnow.models.table.declared.IncidentModel, "get_one", return_value=mock_client):
        response = client.post(
        "/snow/get_ticket", json=json.load(self.test_request)
    )
        assert response.status_code == 200

CodePudding user response:

The way variable assignment works in python, changing aiosnow.models.table.declared.IncidentModel will not change the IncidentModel that you've imported into your python file. You have to do the mocking where you use the object.

So instead of @patch("aiosnow.models.table.declared.IncidentModel"), you want to do @patch("your_python_file.IncidentModel")

  • Related