1

Consider the function in a module.

async def _get_user_mapping():
        async with aiofiles.open('/etc/subuid') as f:
            async for line in f:
                print(line)

And a test class

from atestfile import _get_user_mapping
class TestFS(fake_filesystem_unittest.TestCase):
    def setUp(self):
        self.a_required_class_parameter = True
        self.setUpPyfakefs()
        for file_path in ['/etc/subuid']:
            self.fs.create_file(file_path, contents = """testuser:100000:65536
            testuser2:165536:65536""")

    def test(self):
        if self.a_required_class_parameter:
            asyncio.get_event_loop().run_until_complete(_get_user_mapping())

In this example, the test function should set pyfakefs up to provide a fake file. Unfortunately aiofiles doesn't have access to the file and the printed output is the real file on the system.

Does anyone know how I can patch the aiofiles event loop to use pyfakefs as a fake filesystem? In testing I've found the following snippet using a library called pytest-aiofiles (sounds like what I need, right?) but the example they show:

@pytest.mark.asyncio
async def test_stuff(self):
    filename = 'test'
etc.....

If I add the mark.asyncio decorator to the test class method, the imported function doesn't have access to the generated fake file in the setUp method.

I assume I'm missing something simple so really this can all be broken down into the simple question: How the heck do I test this?

Thanks!

MrBean Bremen
  • 14,916
  • 3
  • 26
  • 46
TechnicalChaos
  • 452
  • 3
  • 21
  • Does adding a call to `super().setUp()` help? – dirn Jan 11 '19 at 12:51
  • No, after calling `super().setUp()` aiofiles still isn't seeing the fake file I've set up. I realised a mistake though, the `afs` object does include the `TestFS` class attributes so it's not overriding the class, I've just renamed `self` as `asf` I guess that decorator is doing nothing. I've updated the question to reflect this now. – TechnicalChaos Jan 11 '19 at 13:13

2 Answers2

0

You could create a class to patch 'aiofiles' altogether?

class FakeAiofiles
    def open():
        # return the open fake file you created in the test

then in your test, use:

def test(self):
    with mock.patch('atestfile.aiofiles', return_value=FakeAiofiles)
        if self.a_required_class_parameter:
            asyncio.get_event_loop().run_until_complete(_get_user_mapping())
Jamie J
  • 1,168
  • 1
  • 9
  • 22
0

Here is how I managed to mock aiofiles used in a file let's say called fofo.py:

with patch("fofo.aiofiles") as patched_aiofiles:
            instance = AsyncMock()
            attrs = {
                "write": AsyncMock(),
                "flush": AsyncMock(),
                "close": AsyncMock(),
            }
            instance.configure_mock(**attrs)
            attrs = {"open": AsyncMock(return_value=instance)}
            patched_aiofiles.configure_mock(**attrs)