0

I readily admit to going a bit overboard with unit testing. While I have passing tests, I find my solution to be inelegant, and I'm curious if anyone has a cleaner solution.

The class being tested:

class Config(): def __init__(self): config_parser = ConfigParser() try: self._read_config_file(config_parser) except FileNotFoundError as e: pass self.token = config_parser.get('Tokens', 'Token', ) @staticmethod def _read_config_file(config): if not config.read(os.path.abspath(os.path.join(BASE_DIR, ROOT_DIR, CONFIG_FILE))): raise FileNotFoundError(f'File {CONFIG_FILE} not found at path {BASE_DIR}{ROOT_DIR}') 

The ugly test:

class TestConfiguration(unittest.TestCase): @mock.patch('config.os.path.abspath') def test_config_init_sets_token(self, mockFilePath: mock.MagicMock): with open('mock_file.ini', 'w') as file: #here's where it gets ugly file.write('[Tokens]\nToken: token') mockFilePath.return_value = 'mock_file.ini' config = Config() self.assertEqual(config.token, 'token') os.remove('mock_file.ini') #quite ugly 

EDIT: What I mean is I'm creating a file instead of mocking one. Does anyone know how to mock a file object, while having its data set so that it reads ascii text? The class is deeply buried. Other than that, the way ConfigParser sets data with .read() is throwing me off. Granted, the test "works", it doesn't do it nicely.

For those asking about other testing behaviors, here's an example of another test in this class:

@mock.patch('config.os.path.abspath') def test_warning_when_file_not_found(self, mockFilePath: mock.MagicMock): mockFilePath.return_value = 'mock_no_file.ini' with self.assertRaises(FileNotFoundError): config.Config._read_config_file(ConfigParser()) 

Thank you for your time.

5
  • That doesn't seem unreasonable. For a different solution, couldn't you just make BASE_DIR, ROOT_DIR, and CONFIG_FILE something that you can set in the module before instantiating a Config() instance? What if your Config class accepted as an optional parameter a path to a configuration file? Commented Sep 7, 2017 at 20:45
  • 1
    I don't think you should be testing ConfigParser. You should rather be testing that _read_config_file does what you mean it to do which is, read a configuration file and raise an exception when it fails to do that. Commented Sep 7, 2017 at 20:49
  • Thanks, @larsks. I see the utility in that, too, but, for now, I'm interested in keeping all of my config-relevant info in one place (i.e., keep the name of the .ini file in the .ini file). What's really giving me trouble, here, is how the ConfigParser class sets file information when it calls .read. I'd rather just patch a mocked file object that takes ascii chars to set data, but the way the class is designed doesn't make that intuitive. Commented Sep 7, 2017 at 20:58
  • @OluwafemiSule - I did test that, as well, with several variants of the above as follows: with self.assertRaises(FileNotFoundError): config.Config._read_config_file(config.ConfigParser()) *(weird syntax to get around the hidden method prefix) Commented Sep 7, 2017 at 21:02
  • *typo above, (i.e., keep the name of the .ini file in the Config file) Commented Sep 7, 2017 at 21:08

1 Answer 1

2

I've found it!

I had to start off with a few imports:from io import TextIOWrapper, BytesIO

This allows a file object to be created: TextIOWrapper(BytesIO(b'<StringContentHere>'))

The next part involved digging into the configparser module to see that it calls open(), in order to mock.patch the behavior, and, here we have it, an isolated unittest!

@mock.patch('configparser.open') def test_bot_init_sets_token(self, mockFileOpen: mock.MagicMock): mockFileOpen.return_value = TextIOWrapper(BytesIO(b'[Tokens]\nToken: token')) config = Config() self.assertEqual(config.token, 'token') 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.