Reputation: 33
I am working with python and new to it. i am trying to write unit tests for my class method where we are using configparse to fetch the config file details located at specific path.
While doing unit testing its giving me an error as KeyError for <configparser.ConfigParser object at 0x000001E8AEBC1F40>
the code for configparser is like
algor_conf = configparser.ConfigParser()
self.algor_conf.read('./path')
self.algor_conf['DynamicProgramingParamaters']['wealth_state_total']
Can anyone please help me to mock this
Upvotes: 1
Views: 2988
Reputation: 10709
Here are some solutions for you. Feel free to choose among the 6 (named test_mock1
to test_mock6
) that best suits your needs.
src.py
import configparser
class MyClass:
def my_method(self):
self.algor_conf = configparser.ConfigParser()
self.algor_conf.read('./path')
return self.algor_conf['DynamicProgramingParamaters']['wealth_state_total']
test_src.py
import configparser
from unittest.mock import patch
import pytest
from src import MyClass
def custom_get_item(key):
if key == 'DynamicProgramingParamaters':
return {'wealth_state_total': 'Just a test 3!'}
else:
raise KeyError(str(key))
class CustomConfigParser1(configparser.ConfigParser):
def __getitem__(self, key):
if key == 'DynamicProgramingParamaters':
return {'wealth_state_total': 'Just a test 4!'}
else:
raise KeyError(str(key))
class CustomConfigParser2(configparser.ConfigParser):
def read(self, filenames, *args, **kwargs):
# Intercept the calls to configparser -> read and replace it to read from your test data
if './path' == filenames:
# Option 1: If you want to manually write the configuration here
self.read_string("[DynamicProgramingParamaters]\nwealth_state_total = Just a test 5!")
# Option 2: If you have a test configuration file
# super().read("./test_path")
else:
super().read(filenames, *args, **kwargs)
@pytest.fixture
def amend_read(mocker): # Requires https://pypi.org/project/pytest-mock/ but you can also change this to just use the builtin unittest.mock
original_func = configparser.ConfigParser.read
def updated_func(self, filenames, *args, **kwargs):
# Intercept the calls to configparser -> read and replace it to read from your test data
if './path' == filenames:
# Option 1: If you want to manually write the configuration here
self.read_string("[DynamicProgramingParamaters]\nwealth_state_total = Just a test 6!")
# Option 2: If you have a test configuration file
# original_func.read(self, "./test_path")
return
return original_func(self, filenames, *args, **kwargs)
mocker.patch('configparser.ConfigParser.read', new=updated_func)
@patch('configparser.ConfigParser')
def test_mock1(config_parser):
# If you just want to mock the configparser without doing anything to its processing results
obj = MyClass()
result = obj.my_method()
print(result)
@patch('configparser.ConfigParser.__getitem__', return_value={'wealth_state_total': 'Just a test 2!'})
def test_mock2(config_parser):
# Change the returned value of configparser['DynamicProgramingParamaters']['wealth_state_total']
obj = MyClass()
result = obj.my_method()
print(result)
@patch('configparser.ConfigParser.__getitem__', side_effect=custom_get_item)
def test_mock3(config_parser):
# Same as test_mock2 only that we instead used a function to write the return
obj = MyClass()
result = obj.my_method()
print(result)
@patch('configparser.ConfigParser', side_effect=CustomConfigParser1)
def test_mock4(config_parser):
# Same as test_mock3 only that we instead used a class to write the return
obj = MyClass()
result = obj.my_method()
print(result)
@patch('configparser.ConfigParser', side_effect=CustomConfigParser2)
def test_mock5(config_parser):
# If have a configuration file for your test data, use this.
obj = MyClass()
result = obj.my_method()
print(result)
def test_mock6(amend_read):
# Same as test_mock5 only that we instead used a function to write the return
obj = MyClass()
result = obj.my_method()
print(result)
Output
$ pytest -rP
test_mock.py ...... [100%]
============================================================================================ PASSES ============================================================================================
__________________________________________________________________________________________ test_mock1 __________________________________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call -------------------------------------------------------------------------------------
<MagicMock name='ConfigParser().__getitem__().__getitem__()' id='140151691208160'>
__________________________________________________________________________________________ test_mock2 __________________________________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call -------------------------------------------------------------------------------------
Just a test 2!
__________________________________________________________________________________________ test_mock3 __________________________________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call -------------------------------------------------------------------------------------
Just a test 3!
__________________________________________________________________________________________ test_mock4 __________________________________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call -------------------------------------------------------------------------------------
Just a test 4!
__________________________________________________________________________________________ test_mock5 __________________________________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call -------------------------------------------------------------------------------------
Just a test 5!
__________________________________________________________________________________________ test_mock6 __________________________________________________________________________________________
------------------------------------------------------------------------------------- Captured stdout call -------------------------------------------------------------------------------------
Just a test 6!
====================================================================================== 6 passed in 0.03s =======================================================================================
Upvotes: 5