5
\$\begingroup\$

Below is a simple class with two methods: the first calls open() to read in a file. The second calls the first method and does error handling.

There is also a corresponding test class that tests the first methods returns data as expected and the other methods test the error handling of potential exceptions thrown by open.

Firstly, I would like to know if these tests are written in the appropriate manner using python3, mocking and patching as I am new to the language and especially to testing.

I also have a query to why mocking an exception to be thrown cannot be handled like the rest of the tests (see last test method)

fileMethods.py

# -*- coding: utf-8 -*-

def readconfig(myfile):
    try:
        filecontent = readfile(myfile)
        #   manipulation of filecontent
    except FileNotFoundError:
        filecontent = "FileNotFoundError"
    except PermissionError:
        filecontent = "PermissionError"
    return filecontent

def readfile(myfile):
    with open(myfile, 'r') as file:
        filecontent = file.read()
    file.close()
    return filecontent

fileMethods_test.py

# -*- coding: utf-8 -*-

import io
import unittest
import unittest.mock as mock
import packageName.fileMethods

class TestIO(unittest.TestCase):

    def test_readfile_returns_string(self):
        mock_file_content = io.StringIO('Content in file')
        with mock.patch('packageName.fileMethods.open', return_value=mock_file_content, create=True):
            result = packageName.fileMethods.readfile('file_to_readin')
        self.assertEqual('Content in file', result)

    def test_readconfig_returns_content(self):
        with mock.patch('packageName.fileMethods.readfile', return_value="string", create=True):
            result = packageName.fileMethods.readconfig('file_to_readin')
        self.assertEqual('string', result)

    def test_readconfig_handles_FileNotFoundError(self):
        with mock.patch('packageName.fileMethods.readfile', side_effect=FileNotFoundError, create=True):
            result = packageName.fileMethods.readconfig('file_to_readin')
        self.assertEqual('FileNotFoundError', result)

    def test_readconfig_handles_PermissionError(self):
        with mock.patch('packageName.fileMethods.readfile', side_effect=PermissionError, create=True):
            result = packageName.fileMethods.readconfig('file_to_readin')
        self.assertEqual('PermissionError', result)

    # Does not work
    def test_readconfig_throws_OSError(self):
        with mock.patch('packageName.fileMethods.readfile', side_effect=OSError, create=True):
            self.assertRaises(OSError, packageName.fileMethods.readconfig('file_to_readin'))
\$\endgroup\$
3
\$\begingroup\$

The design of the readconfig() function is seriously problematic. It makes no sense to convert caught exceptions into data, as if the file literally contained the string "FileNotFoundError" or "PermissionError". If you don't have a good way to handle an exception, the right thing to do is let it propagate.

The readfile() function should be simpler:

def read_file(path):
    with open(path) as file:
        return file.read()

To note:

  • Since "read file" is two words in English, it should not be written as a compound word.
  • myfile could be better named. It's a filesystem path, not a file handle. Also, who is "me" in this case?
  • The 'r' mode is the default.
  • You don't need to call file.close() on an already closed file. The whole point of the with block is that the file is automatically closed when exiting the block.
\$\endgroup\$
  • \$\begingroup\$ Thanks for the feedback especially with my read_file method. I do not intent to return the error types of a string in an application. I was trying to understand how to test and account for exceptions thrown in a method/function which is used. If like you say I let the error propagate like i did in the read_file method how would I test that an error has been thrown? I attempted to do this in the last test method test_readconfig_throws_OSError which does not work. \$\endgroup\$ – SJC Aug 1 '16 at 18:55
  • 1
    \$\begingroup\$ On Code Review, we take your posted code literally and seriously. Anyway, the answer is not hard to find. \$\endgroup\$ – 200_success Aug 1 '16 at 18:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.