Chuck Chuck - 4 months ago 12
Python Question

How do I test a method that requires a file's presence?

For the first time, I'm using Python to create a library, and I'm trying to take the opportunity in this project to learn unit testing. I've written a first method and I want to write some unit tests for it. (Yes, I know that TDD requires I write the test first, I'll get there, really.)

The method is fairly simple, but it expects that the class has a

file
attribute set, that the attribute points to an existing file, and that the file is an archive of some sort (currently only working with zip files, tar, rar, etc., to be added later). The method is supposed to return the number of files in the archive.

I've created a folder in my project called
files
that contains a few sample files, and I've manually tested the method and it works as it should so far. The manual test looks like this, located in the
archive_file.py
file:

if __name__ == '__main__':
archive = ArchiveFile()

script_path = path.realpath(__file__)
parent_dir = path.abspath(path.join(script_path, os.pardir))
targ_dir = path.join(parent_dir, 'files')
targ_file = path.join(targ_dir, 'test.zip' )

archive.file = targ_file

print(archive.file_count())


All I do then is make sure that what's printed is what I expect given the contents of
test.zip
.

Here's what
file_count
looks like:

def file_count(self):
"""Return the number of files in the archive."""
if self.file == None:
return -1

with ZipFile(self.file) as zip:
members = zip.namelist()
# Remove folder members if there are any.
pruned = [item for item in members if not item.endswith('/')]
return len(pruned)


Directly translating this to a unit test seems wrong to me for a few reasons, some of which may be invalid. I'm counting on the precise location of the test files in relation to the current script file, I'll need a large sample of manually created archive files to make sure I'm testing enough variations, and, of course, I'm manually comparing the returned value to what I expect because I know how many files are in the test archive.

It seems to me that this should be automated as much as possible, but it also seems that doing so is going to be very complicated.

What's the proper way to create unit tests for such a class method?

Answer

Oasiscircle and dm03514 were very helpful on this and lead me to the right answer eventually, especially with dm's answer to a followup question.

What needs to be done is to use the mock library to create a fake version of ZipFile that won't object to there not actually being a file, but will instead return valid lists when using the nameslist method.

@unittest.mock.patch('comicfile.ZipFile')
def test_page_count(self, mock_zip_file):
    comic_file = ComicFile()
    members = ['dir/', 'dir/file1', 'dir/file2']
    mock_zip_file.return_value.__enter__.return_value.namelist.return_value \
                = members
    self.assertEqual(2, self.comic_file.page_count())

The __enter__.return_value portion above is necessary because in the code being tested the ZipFile instance is being created within a context manager.

Comments