cheuk209
cheuk209

Reputation: 51

Why is my function still making API calls even though I am mock.patching it?

I have a function that makes a query to an API (a pseudo-API hosted locally) to retrieve a list of values. Nothing fancy.

When trying to test a function containing this API call, I obviously want to mock it. But it is still making calls to the actual API instead of returning mock values that I created in the test itself.

def display_unhealthy_services():
    healthy_entries = {}
    unhealthy_services = []
    all_ip_addresses = retrieve_all_ip_addresses()
    for ip in all_ip_addresses:
        ip_info = get_ip_status_info(ip)
        if ip_info[2] == "Healthy":
            if ip_info[1] not in healthy_entries:
                healthy_entries[ip_info[1]] = 1
            else:
                healthy_entries[ip_info[1]] += 1
    for key, values in healthy_entries.items():
        if values < 2:
            unhealthy_services.append(key)
    output_string = "\n".join(unhealthy_services)
    if output_string != "":
        click.echo("Services with less than 2 healthy instances include: ")
        click.echo(output_string)
    else:
        print("Cat", all_ip_addresses)
        click.secho("Your DevOps engineers are too good! There are no unhealthy services!",fg="black", bg="yellow") 

Here is my pytest:

@mock.patch("get_average.retrieve_all_ip_addresses", return_value=["8.8.8.8", "8.8.8.9"])
@mock.patch("get_ip_info.get_ip_status_info")
def test_display_unhealthy_services_if_they_exist(mock_ip_status, mock_ip_addresses):
    # Set up the mock return values for the functions
    mock_ip_status.side_effect = [
        ("8.8.8.8", "service1", "Healthy", 40, 50),
        ("8.8.8.9", "service2", "Unhealthy", 90, 30)
    ]
    # Invoke the display_unhealthy_services() function using CliRunner
    runner = CliRunner()
    result = runner.invoke(display_unhealthy_services)
    # Assert that the output is as expected
    expected_output = "Services with less than 2 healthy instances include: \nservice2"
    assert expected_output in result.output
    # check if the function calls are being made correctly
    mock_ip_status.assert_called()
    mock_ip_addresses.assert_called()

As you can see, I am trying to retrieve all IP addresses, but in my test I am expecting to only get ["8.8.8.8", "8.8.8.9"].

But when I run the test, it still querys the actual API and I cannot see ["8.8.8.8", "8.8.8.9"] at all.

I've tried rewriting it in pytest several times but to no avail. It keeps on returning values that the function would get from querying the actual API.

TLDR: I have function A and function B.

Function A is in function B. When function A gets called when function B gets called.

Now how do I test function B, whilst mocking values of function A?

I want to control the output of function A, so that when function B gets called, it will use the input values that I decide

Upvotes: 4

Views: 1336

Answers (1)

User051209
User051209

Reputation: 2558

Pay attention to mock in the right place

I don't test your code on my system, but I think that you are patching the functions in the wrong modules.

You have to mock the objects imported by the module that contains the display_unhealthy_services function and not the objects in the file where they are defined.

I suppose that the production function display_unhealthy_services is defined in the module display_services.py and in this module are present the following import:

from get_average import retrieve_all_ip_addresses
from get_ip_info import get_ip_status_info

With this hypothesis, in the test code, the patch() should become:

# NOTE the presence of display_services instead of get_average
@mock.patch("display_services.retrieve_all_ip_addresses", return_value=["8.8.8.8", "8.8.8.9"])

# NOTE the presence of display_services instead of get_ip_info
@mock.patch("display_services.get_ip_status_info")

I didn't check the other part of your test function, but I think that this changes could help you to set the return value for your mock objects.

Upvotes: 6

Related Questions