Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing of "renaming files" in Python

Suppose I have the following function and I need to unit test it:

def move_files_to(files, path):
for file in files:
    os.rename(file, path + "/" + file)

my thoughts on this topic:

Mocking os.rename would lead to no outcome, on the other hand, having files and renaming them would be "doing I/O" which should be avoided in unit tests. Please correct me, if I am wrong. Does testing make sense here?

like image 219
David Avatar asked Apr 20 '26 17:04

David


2 Answers

There is no need to test os.rename, but your code needs to be tested. In your particular case the simplest way is to patch os.rename:

from unittest.mock import patch

def test_move_files():
    files = ['a.txt', 'b.txt']
    path = 'old'
    expected = [(('a.txt', 'old/a.txt'),),
                (('b.txt', 'old/b.txt'),)]
    with patch('os.rename') as rename:
        move_files_to(files, path)
        assert rename.call_args_list == expected
like image 129
atihonruk Avatar answered Apr 22 '26 06:04

atihonruk


My recommendation would be

create a DirectoryTestCase(unittest.TestCase) with

  1. a setup that i) removes a (non-tracked) directory if it exists (e.g. _tests under tests/) ii) creates an empty directory and iii) copies (test) files to it.
  2. a teardown that removes the directory

Second, create the tests under that test case. You can test moving to same directory (rename), to a new directory (under the test directory, should it create if it does not exist or fail?) move a symlink, etc., all under the encapsulation of the existing directory.

If you want tests to run in parallel, give the directory a unique (random but valid) name.

Example code:

import unittest
import shutil
import os
import string
import random


class DirectoryTestCase(unittest.TestCase):
    test_files = ['file.txt']

    def setUp(self) -> None:
        unique_string = ''.join(random.choices(string.digits, k=10))

        self.path = os.path.join(os.path.dirname(__file__), '_temp' + unique_string)

        shutil.rmtree(self.path, ignore_errors=True)

        os.makedirs(self.path, exist_ok=True)

        for file in self.test_files:
            shutil.copy(file, self.path)

    def tearDown(self) -> None:
        shutil.rmtree(self.path, ignore_errors=True)

    def test_basic(self):
        # validate the existence of the file, not an actual test.
        self.assertTrue(os.path.exists(os.path.join(self.path, 'file.txt')))
like image 34
Jorge Leitao Avatar answered Apr 22 '26 05:04

Jorge Leitao