5

I am trying to write a unit test to a class init that reads from a file using readlines:

class Foo:
    def __init__(self, filename):
         with open(filename, "r") as fp:
             self.data = fp.readlines()

with sanity checks etc. included.

Now I am trying to create a mock object that would allow me to test what happens here.

I try something like this:

TEST_DATA = "foo\nbar\nxyzzy\n"
with patch("my.data.class.open",  mock_open(read_data=TEST_DATA), create=True)
    f = Foo("somefilename")
    self.assertEqual(.....)

The problem is, when I peek into f.data, there is only one element:

["foo\nbar\nxyzzy\n"]

Which means whatever happened, did not get split into lines but was treated as one line. How do I force linefeeds to happen in the mock data?

Hannu
  • 11,685
  • 4
  • 35
  • 51
  • Did you forget to call `splitlines` somewhere? – Mad Physicist Mar 17 '18 at 16:05
  • What are your imports? – Mad Physicist Mar 17 '18 at 16:06
  • mock `readlines()` in the title and mock `open`using `mock_open` in the details? – Gang Mar 17 '18 at 16:21
  • I did not need splitlines as I called readlines, where splitlines is implicit. I managed to fix this by replacing the statement with self.data = fp.read.splitlines(). It works exactly the same way and the unit test now passes as well. – Hannu Mar 19 '18 at 09:17
  • Possible duplicate of [How do I mock an open used in a with statement (using the Mock framework in Python)?](https://stackoverflow.com/questions/1289894/how-do-i-mock-an-open-used-in-a-with-statement-using-the-mock-framework-in-pyth) – Dušan Maďar May 20 '19 at 14:57

2 Answers2

7

This will not work with a class name

with patch("mymodule.class_name.open",

But this will work by mocking the builtin directly, builtins.open for python3

@mock.patch("__builtin__.open", new_callable=mock.mock_open, read_data=TEST_DATA)
def test_open3(self, mock_open):
   ...

or this without class by mocking the module method

 def test_open(self):
     with patch("mymodule.open", mock.mock_open(read_data=TEST_DATA), create=True):
         ...
Gang
  • 2,658
  • 3
  • 17
  • 38
0

@Gang's answer pointed me to the right direction but it's not a complete working solution. I have added few details here which makes it a working code without any tinkering.

# file_read.py

def read_from_file():
    # Do other things here
    filename = "file_with_data"
    with open(filename, "r") as f:
        l = f.readline()
    return l
# test_file_read.py

from file_read import read_from_file
from unittest import mock
import builtins

@@mock.patch.object(builtins, "open", new_callable=mock.mock_open, read_data="blah")
def test_file_read(mock_file_open):
    output = read_from_file()
    expected_output = "blah"
    assert output == expected_output
rrlamichhane
  • 1,435
  • 2
  • 18
  • 34