views:

484

answers:

3

How do I test the following code with mocks (using mocks, the patch decorator and sentinels provided by Michael Foord's Mock framework):

def testme(filepath):
    with open(filepath, 'r') as f:
        return f.read()
+2  A: 

When used in a with statement, open returns a context manager. So the mock of open needs to also. A context manager must have an __enter__() and __exit__() method. The __enter__() method needs to return the file handle mock that will register the call to the read() method in the code above:

@patch('__builtin__.open')
def test_testme(self, open_mock):
    #
    # setup
    #
    context_manager_mock = Mock()
    open_mock.return_value = context_manager_mock
    file_mock = Mock()
    file_mock.read.return_value = sentinel.file_contents
    context_manager_mock.__enter__.return_value = file_mock

    #
    # exercise
    #
    result = cbot.testme(sentinel.filepath)

    #
    # verify
    #
    self.assertEquals(result, sentinel.file_contents)
    self.assertEquals(open_mock.call_args,
                      ((sentinel.filepath, 'r'), {}))
    self.assertEquals(context_manager_mock.method_calls,
                      [('__enter__', (), {}),
                       ('__exit__', (None, None, None), {})])
    self.assertEquals(file_mock.method_calls, [('read', (), {})])
Daryl Spitzer
You don't have to make it CW.
Brad Gilbert
This doesn't work; the mock doesn't have the \_\_enter\_\_ attribute, and that's because it automatically omits any magic values (__'d).Do you happen to have a working solution for this now? That'd be really cool.
Chris R
This did work at the the time I wrote this. Perhaps changes have been made to Mock that broke it. I'm no longer set up with Mock--I'll make time to re-create this with the latest version of Mock and update this.
Daryl Spitzer
Answer no longer valid
Casey
A: 

Considering Mock() doesn't have __enter__ attribute set by default, how can the line

context_manager_mock.__enter__.return_value = file_mock

work?

How does with find __exit__ if it has not been declared yet?

bebraw
Please, use "Ask Question" button.
J.F. Sebastian
A: 

Updated Daryl's answer to fix changes to Mock class.

@patch('__builtin__.open')
def test_testme(self, open_mock):
    #
    # setup
    #
    context_manager_mock = Mock()
    open_mock.return_value = context_manager_mock
    file_mock = Mock()
    file_mock.read.return_value = sentinel.file_contents
    enter_mock = Mock()
    enter_mock.return_value = file_mock
    exit_mock  = Mock()
    setattr( context_manager, '__enter__', enter_mock )
    setattr( context_manager, '__exit__', exit_mock )

    #
    # exercise
    #
    result = cbot.testme(sentinel.filepath)

    #
    # verify
    #
    self.assertEquals(result, sentinel.file_contents)
    self.assertEquals(open_mock.call_args,
                      ((sentinel.filepath, 'r'), {}))
    self.assertEquals(context_manager_mock.method_calls,
                      [('__enter__', (), {}),
                       ('__exit__', (None, None, None), {})])
    self.assertEquals(file_mock.method_calls, [('read', (), {})])
Casey