0

I use py.test for unit testing. I have something just like this:

Class Site(object):

    def set_path(self):
        """Checks if the blog folder exists. Creates new folder if necessary."""        
        if not os.path.isdir(self.user_dir):
            print "Creating new directory:", self.user_dir
            os.mkdir(self.user_dir)
        else:
            print "\n**", self.user, "directory already exists."

How can I unit test this without touching the file system?

Is there a general rule of thumb when dealing with such problems?

I've thought about it for a long time, and I cannot think of a solution.

My application will contain many file system accesses, and I don't know how to incorporate those methods when unit testing.

I want to specifically test that a folder is created in the correct path. The path is self.user_dir.

I also have a download function, that downloads an image into self.user_dir. I want to test that the image is downloaded. How would one go about testing this?

BBedit
  • 7,037
  • 7
  • 37
  • 50

1 Answers1

3

There are two approaches you could use, mocking (preferred) or isolation:

Mocking:

At test time, replace os.mkdir with a "mock" version:

class MockMkdir(object):
    def __init__(self):
        self.received_args = None
    def __call__(*args):
        self.received_args = args

class MyTest(unittest.TestCase):
    def setUp():
        self._orig_mkdir = os.mkdir
        os.mkdir = MockMkdir()
    def tearDown():
        os.mkdir = self._orig_mkdir
    def test_set_path(self):
        site = Site()
        site.set_path('foo/bar')
        self.assertEqual(os.mkdir.received_args[0], 'foo/bar')

Mock is a library that helps you do this kind of thing more elegantly and with fewer lines of code.

Isolation

Use chroot to run your unittests within an isolated filesystem. After running each test case, revert the filesystem to a clean state. This is something of a sledgehammer approach but for testing very complicated libraries it might be easier to get this approach to work.

Alex Flint
  • 6,040
  • 8
  • 41
  • 80