How to patch a constant in python

后端 未结 3 1790
南旧
南旧 2020-12-18 18:01

I have two different modules in my project. One is a config file which contains

LOGGING_ACTIVATED = False

This constant is used in the seco

相关标签:
3条回答
  • 2020-12-18 18:23

    If the if LOGGING_ACTIVATED: test happens at the module level, you need to make sure that that module is not yet imported first. Module-level code runs just once (the first time the module is imported anywhere), you cannot test code that won't run again.

    If the test is in a function, note that the global name used is LOGGING_ACTIVATED, not config.LOGGING_ACTIVATED. As such you need to patch out main.LOGGING_ACTIVATED here:

    @patch("main.LOGGING_ACTIVATED", True)
    

    as that's the actual reference you wanted to replace.

    Also see the Where to patch section of the mock documentation.

    You should consider refactoring module-level code to something more testable. Although you can force a reload of module code by deleting the module object from the sys.modules mapping, it is plain cleaner to move code you want to be testable into a function.

    So if your code now looks something like this:

    if LOGGING_ACTIVATED:
        amqp_connector = Connector()
    

    consider using a function instead:

    def main():
        global amqp_connector
        if LOGGING_ACTIVATED:
            amqp_connector = Connector()
    
    main()
    

    or produce an object with attributes even.

    0 讨论(0)
  • 2020-12-18 18:25

    Found this thread while having a similar issue, what worked for me:

    from unittest import mock
    
    @mock.patch('<name_of_module>.<name_of_file>.<CONSTANT_NAME>', <value_to_replace_with>)
    class Test_<name_of_test_suit>(unittest.TestCase):
    
      def test_<name_of_test>(self):
        <test_body>
    

    Keep in mind that you would need and __init__.py to treat directories containing files as packages. https://docs.python.org/3/tutorial/modules.html#:~:text=The%20__init__.py,on%20the%20module%20search%20path.

    P.S. Have a look at https://chase-seibert.github.io/blog/2015/06/25/python-mocking-cookbook.html

    0 讨论(0)
  • 2020-12-18 18:26

    The problem you are facing is that you are mocking where it is defined, and you should patch where is it used.

    Mock an item where it is used, not where it came from.

    I leave you some example code, so you can catch the idea.

    project1/constants.py

    INPUT_DIRECTORY="/input_folder"
    

    project1/module1.py

    from project1.constants import INPUT_DIRECTORY
    import os
    
    def clean_directories():
        for filename in os.listdir(INPUT_DIRECTORY):
            filepath = os.path.join(directory, filename)
            os.remove(filepath)
    

    project1/tests/test_module1.py

    import mock, pytest
    
    def test_clean_directories(tmpdir_factory):
        """Test that folders supposed to be emptied, are effectively emptied"""
    
        # Mock folder and one file in it
        in_folder = tmpdir_factory.mktemp("in")
        in_file = in_folder.join("name2.json")
        in_file.write("{'asd': 3}")
    
        # Check there is one file in the folder
        assert len([name for name in os.listdir(in_folder.strpath) if os.path.isfile(os.path.join(path, name))]) == 1
    
        # As this folder is not a parameter of the function, mock it.
        with mock.patch('project1.module1.INPUT_DIRECTORY', in_folder.strpath):
            clean_directories()
    
        # Check there is no file in the folder
        assert len([name for name in os.listdir(in_folder.strpath) if os.path.isfile(os.path.join(path, name))]) == 0
    

    So the importan line would be this one:

    with mock.patch('project1.module1.INPUT_DIRECTORY', in_folder.strpath):
    

    See, value is mocked where it is used, and not in constants.py (where it is defined)

    0 讨论(0)
提交回复
热议问题