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())
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 with the
mock_open function using the
import pytest import script from unittest.mock import patch, mock_open, call def test_file_logic(mock_open_obj): read_data(input_path, output_path)
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)
any_order is important because open go through various stack trace like
__enter__ and doesn't appear in the order.