Python unit test how to use Mox to mock the gzip with statement

2.4k views Asked by At

In Python, how do I mock an object created in a with statement using mox unit test library

Code

class MyCode:
    def generate_gzip_file(self):
        with gzip.GzipFile('file_name.txt.gz','wb') as f:
             f.write('data')

Unit Test

class MyCodeTest(unittest.TestCase):
    def test_generate_gzip_file(self):
        mox = mox.Mox()
        mock_gzip_file = self.mox.CreateMock(gzip.GzipFile)
        mox.StubOutWithMock(gzip, 'GzipFile')
        gzip.GzipFile('file_name.txt.gz','wb').AndReturn(mock_file)
        mock_gzip_file.write('data')
        mox.ReplayAll()
        MyCode().generate_gzip_file()
        mox.VerifyAll()

I get the error AttributeError: __exit__ on line

with gzip.GzipFile('file_name.txt.gz','wb') as f:
1

There are 1 answers

0
srgerg On BEST ANSWER

DSM is correct that the mocked instance of gzip.GzipFile isn't ending up with a __exit__ method for some reason. You'll get exactly the same error if you forget to define __exit__ on a class you use with a with statement. For example:

>>> class C(object):
...   def __enter__(self):
...     return self
... 
>>> with C() as c:
...   pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: __exit__

Fortunately, you can work around the problem by using Mox's CreateMockAnything() method to create a mock_gzip_file object that doesn't enforce a particular interface. You'll need to be careful to ensure that you set up the expectations for the mock_gzip_file object correctly (i.e. that you set up expectations for when and how the __enter__() and __exit__(...) methods will be called). Here's an example that worked for me:

import gzip
import mox
import unittest

class MyCode:
    def generate_gzip_file(self):
        with gzip.GzipFile('file_name.txt.gz', 'wb') as f:
             f.write('data')

class MyCodeTest(unittest.TestCase):
    def test_generate_gzip_file(self):
        mymox = mox.Mox()
        mock_gzip_file = mymox.CreateMockAnything()
        mymox.StubOutWithMock(gzip, 'GzipFile')
        gzip.GzipFile('file_name.txt.gz', 'wb').AndReturn(mock_gzip_file)
        mock_gzip_file.__enter__().AndReturn(mock_gzip_file)
        mock_gzip_file.write('data')
        mock_gzip_file.__exit__(None, None, None).AndReturn(None)
        mymox.ReplayAll()

        MyCode().generate_gzip_file()
        mymox.VerifyAll()

if __name__ == '__main__':
    unittest.main()

When I run this I get:

.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK