Home > Net >  Unit test function without return value
Unit test function without return value

Time:10-12

I have one function like this one:

def function(df, path_write):
    df['A'] = df['col1'] * df['col2']
    write(df, path)

The function is not that simple but the question is, how can i make a unit test if the function do not return any value??

If the function returns the new df it's simple, just make:

assert_frame_equal from the library from pandas.testing import assert_frame_equal and mock the write method.

But without that return, how can i test the df line??

CodePudding user response:

In general, I can think of only two kinds of functions: Those that return a value and those that produce side-effects.

With the second kind, you typically don't want the side-effects to actually happen during testing. For example, if your function writes something to disk or sends some data to some API on the internet, you usually don't want it to actually do that during the test, you only want to ensure that it attempts to do the right thing.

To roll with the example of disk I/O as a side-effect: You would usually have some function that does the actual writing to the filesystem that the function under testing calls. Let's say it is named write. The typical apporach would be to mock that write function in your test. Then you would need to verify that that mocked write was called with the arguments you expected.

Say you have the following code.py for example:

def write(thing: object, path: str) -> None:
    print("Some side effect like disk I/O...")


def function(thing: object, file_name: str) -> None:
    ...
    directory = "/tmp/"
    write(thing, path=directory   file_name)

To test function, I would suggest the following test.py:

from unittest import TestCase
from unittest.mock import MagicMock, patch

from . import code


class MyTestCase(TestCase):
    @patch.object(code, "write")
    def test_function(self, mock_write: MagicMock) -> None:
        test_thing = object()
        test_file_name = "test.txt"
        self.assertIsNone(code.function(test_thing, test_file_name))
        mock_write.assert_called_once_with(
            test_thing,
            path="/tmp/"   test_file_name,
        )

Check out unittest.mock for more details on mocking with the standard library. I would strongly advise to use the tools there and not do custom monkey-patching. The latter is certainly possible, but always carries the risk that you forget to revert the patched objects back to their original state after every test. That can break the entire rest of your test cases and depending on how you monkey-patched, the source of the resulting errors may become very hard to track down.

Hope this helps.

CodePudding user response:

In the mock for write() you can add assert statements to ensure the form of df is as you would expect. For example:

def _mock_write(df, path):
    assert path == '<expected path value>'
    assert_frame_equal(df, <expected dataframe>)

So the full test case would be:

def test_function(self, monkeypatch):
    # Define mock function
    def _mock_write(df, path):
        assert path == '<expected path value>'
        assert_frame_equal(df, <expected dataframe>)

    # Mock write function
    monkepyatch.setattr(<MyClass>, 'write', _mock_write)

    # Run function to enter mocked write function
    function(test_df, test_path_write)

N.B. This is assuming you are using pytest as your test runner which supports the set up and tear down of monkeypatch. Other answers show the usage for the standard unittest framework.

  • Related