13

I'm trying to get comfortable with mocking in Python and I'm stumbling while trying to mock the following function.

helpers.py

from path import Path

def sanitize_line_ending(filename):
    """ Converts the line endings of the file to the line endings
        of the current system.
    """
    input_path = Path(filename)

    with input_path.in_place() as (reader, writer):
        for line in reader:
            writer.write(line)

test_helpers.py

@mock.patch('downloader.helpers.Path')
def test_sanitize_line_endings(self, mock_path):
    mock_path.in_place.return_value = (1,2)
    helpers.sanitize_line_ending('varun.txt')

However I constantly get the following error:

ValueError: need more than 0 values to unpack

Given that I've set the return value to be a tuple, I don't understand why Python is unable to unpack it.

I then changed my code to have test_sanitize_line_endings store print return value of input_path.in_place() and I can see that the return value is a MagicMock object. Specifically it prints something like <MagicMock name='Path().in_place()' id='13023525345'> If I understand things correctly, what I want is to have mock_path be the MagicMock which has an in_place function that returns a tuple.

What am I doing wrong, and how can I go about correctly replacing the return value of input_path.in_place() in sanitize_line_ending.

4 Answers 4

9

After much head scratching and attending a meetup I finally came across this blog post that finally solved my issue.

The crux of the issue is that I was not mocking the correct value. Since I want to replace the result of a function call the code I needed to have written was:

@mock.patch('downloader.helpers.Path')
def test_sanitize_line_endings(self, mock_path):
    mock_path.return_value.in_place.return_value = (1,2)
    helpers.sanitize_line_ending('varun.txt')

This correctly results in the function being able to unpack the tuple, it then immediately fails since like @didi2002 mentioned this isn't a context manager. However I was focussed on getting the unpacking to work, and after I was able to achieve that replaced the tuple with a construct with the appropriate methods.

4

I struggled with this error ValueError: need more than 0 values to unpack for several hours. But the problem was not in the way I set the mock up (the correct way was described by @varun-madiath here).

It was in using @mock.patch() decorator:

@mock.patch('pika.BlockingConnection') @mock.patch('os.path.isfile') @mock.patch('subprocess.Popen') def test_foo(self, **mocked_connection**, mock_isfile, **mock_popen**):

The order of parameters must be reversed! See python docs.

0

To be valid, the return value of input_path.in_place() must be an object that has an __enter__ method that returns a tuple.

This is a (very verbose) example:

def test():
    context = MagicMock()
    context.__enter__.return_value = (1, 2)

    func = MagicMock()
    func.in_place.return_value = context

    path_mock = MagicMock()
    path_mock.return_value = func

    with patch("path.Path", path_mock):
        sanitize_line_ending("test.txt")
0

try this for return tuple from mocked function:

ret = (1, 2)
type(mock_path).return_value = PropertyMock(return_value = ret)
1
  • type(mock_path).return_value = PropertyMock(return_value = (1, 2)) Commented Aug 28, 2018 at 8:49

Not the answer you're looking for? Browse other questions tagged or ask your own question.