Home > Software design >  Unit test: patching os join overwrites os join in test file
Unit test: patching os join overwrites os join in test file

Time:10-23

I'm writing a unit test for a function that creates a folder using os.makedirs and os.path.join. When I'm trying to mock the specific os.path.join used in the class BuilddataHelpers it also patches the os.path.join in the test file. How can I specifically patch the os.path.join in the file that is being tested while still using os.path.join in the test file?

datamanager/builddatahelpers.py

import os
PATH = 'not/test/data/either'

class BuilddataHelpers:
    def create_folder(self, folder_name):
        create_folder_path = os.path.join(PATH, folder_name)
        os.makedirs(create_folder_path, exist_ok = True)
        return path

datamanager/datamanager.py

class DataManager(BuilddataHelpers):
     ...

test_builddatahelpers.py

import os

class TestBuilddataHelpers(unittest.TestCase):
    global TEST_PATH 
    TEST_PATH = 'tests/data'

    def setUp(self):
        super(TestBuilddataHelpers, self).setUp()
        self.dm = DataManager()
    
    @patch('datamanager.builddatahelpers.os.path.join', return_value=TEST_PATH)
    def test_create_folder_structure_on_ftp(self, mock_os_join):
        folder_name = 'test_folder'
        self.dm.create_folder(folder_name)
        self.assertTrue(os.path.exists(os.path.join(TEST_PATH, folder_name)))
        print('SUBFOLDER PATH: ' , os.path.join('not', 'test', 'data'))

outputs SUBFOLDER PATH: tests/data. The folder is still created since os.makedirs is not patched.

CodePudding user response:

In your tested code, you do import os, which means that os.path.join will be used instead of a local reference. So if you do:

    @patch('datamanager.builddatahelpers.os.path.join', return_value=TEST_PATH)
    def test_create_folder_structure_on_ftp(self, mock_os_join):
        ...

this is exactly the same as writing:

    @patch('os.path.join', return_value=TEST_PATH)
    def test_create_folder_structure_on_ftp(self, mock_os_join):
        ...

That first version only would make sense if you had used from x import y in the tested code. In your case, os.path.join will be patched for all modules using it. If you want to use the original os.path.join, you have to cache it somewhere. One possibility is to just use:

from os.path import join

...
    @patch('os.path.join', return_value=TEST_PATH)
    def test_create_folder_structure_on_ftp(self, mock_os_join):
        folder_name = 'test_folder'
        self.dm.create_folder(folder_name)
        self.assertTrue(os.path.exists(join(TEST_PATH, folder_name)))
        print('SUBFOLDER PATH: ' , join('not', 'test', 'data'))

The local reference to os.path.join saved in join will not be patched. You could also cache it as a class variable:

import os

class TestBuilddataHelpers(unittest.TestCase):
    TEST_PATH = 'tests/data'
    join = os.path.join
...

    self.assertTrue(os.path.exists(self.join(TEST_PATH, folder_name)))

which shall have the same effect.

  • Related