pythonunit-testingservicenow-rest-api

How to mock client object


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.


Solution

  • 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