DEV Community

Bhavani Ravi
Bhavani Ravi

Posted on • Originally published at bhavaniravi.Medium on

Pytest — How to test that a function is called with specific parameters?

Unit tests are written to test single possible units of your system. When you interact with an external system, such as reading a file from AWS S3, your tests must run without any dependency on this external system. The standard testing practice for this use case is to mock the function interacting with this external system and ensure everything else works as expected.

To illustrate this example, Let’s start with a simple Python function that reads data from a CSV and writes it to another CSV.

def read_data(input_path=None, output_path=None): 
    input_path = input_path or "input_data/data.csv" 
    output_path = output_path or "output_data/output.csv" 
    with open(input_path, "r") as fin: 
        with open(output_path, "w") as fout: 
            fout.write(fin.read())
Enter fullscreen mode Exit fullscreen mode

The test case we are interested in is to ensure that the open function is called with the passed param and not the default. Since open is a Python built-in, we don't have to test its logic explicitly.

Let’s start by mocking the builtins.open built-in mock_open function using the patch decorator.

import pytest
import script

from unittest.mock import patch, mock_open, call

[@patch](http://twitter.com/patch)("builtins.open", new_callable=mock_open, read_data="data")
def test_file_logic(mock_open_obj):
    read_data(input_path, output_path)
Enter fullscreen mode Exit fullscreen mode

Next, we have to ensure the read_data function calls the open method with the expected input and output path. assert_has_calls is a function of the mock object, which will check the trace of calls and ensure that the call with specific params is present.

import pytest 
import script 
from unittest.mock import patch, mock_open, call 

@patch("builtins.open", new_callable=mock_open, read_data="data") def test_file_logic(mock_open_obj): 
    read_data(input_path, output_path)     
    mock_open_obj.assert_has_calls([call(expected_output_path, "w"),         
                                    call(expected_input_path, "r")], 
                                    any_order=True)
Enter fullscreen mode Exit fullscreen mode

The any_order is important because open go through various stack trace like __enter__ and doesn't appear in the order.

Originally published at https://thelearning.dev.

Top comments (0)