On Monday, March 12, 2018 at 4:51:53 AM UTC-4, Tim Golden wrote:
> I'm contributing to a codebase which makes heavy use of mock in the test 
> suite, a technique which I'm aware of but have used only rarely. In one 
> situation it uses mock.mock_open(read_data="...") and then asserts again 
> mock_open.return_value.read.call_count.
> 
> A code change I've made results in an increase in the call count but 
> also the open() I've introduced opens the file in binary mode and does 
> something with the resulting data.
> 
> Hugely simplified, the new code and unchanged test looks like this:
> 
> import os, sys
> import unittest
> from unittest import mock
> 
> def read_file(filename):
> 
>      #
>      # This section is new
>      #
>      with open(filename, "rb") as f:
>          text = f.read()
>          if text.startswith(b"#"):
>              pass
> 
>      with open(filename) as f:
>          text = f.read()
>          if text.startswith("#"):
>              pass
> 
>      return text
> 
> class TestS(unittest.TestCase):
> 
>      def test_read_file(self):
>          mock_open = mock.mock_open(read_data="abc")
>          with mock.patch('builtins.open', mock_open):
>              data = read_file("abc")
>          assert mock_open.return_value.read.call_count == 1
> 
> if __name__ == '__main__':
>      unittest.main()
> 
> 
> I would expect the test to fail because of the call_count change. But in 
> fact it errors out because the newly-added "if test.startswith()" 
> receives a string, not bytes, from the Mock's read_data functionality.
> 
> Ignore for the moment any question of changing the read_file 
> implementation to assist testing. And leave aside the question of 
> whether a mock_open is really a good test approach here.
> 
> Is there any way in which I can have the mock_open object return bytes 
> for the first open and a string for the second? I've looked at setting a 
> side_effect function against the mock_open.return_value.read Mock, but I 
> can't see a way of having the function know whether it's supposed to be 
> returning bytes or string.
> 
> 
> TJG

2 ways come to mind:

1. Have the side effect function check something that differentiates
those 2 calls. In this case, checking mode argument value should work.

2. Rely on the order of calls with a local side effect function
closing over a call number. Something like this (untested):

    def test_read_file(self):
        call_number = 0

        def open_side_effect(filename, mode='r'):
            call_number += 1

            if call_number == 1:
                 return b'some bytes'
            if call_number == 2:
                 return 'some string'

         with mock.patch(..., side_effect=open_side_effect):
              # test

Regards,
Igor.
-- 
https://mail.python.org/mailman/listinfo/python-list

Reply via email to