Python: Mock a module without importing it or needing it to exist

后端 未结 2 557
梦如初夏
梦如初夏 2021-02-19 01:28

I am starting to use a python mock library for my testing. I want to mock a module that is imported within the namespace of the module under test without actually importing it o

相关标签:
2条回答
  • 2021-02-19 01:51

    I had a similar problem where the helpers library couldn't be loaded as it needed special hardware. Rather than make radical changes to the code that you want to test, an alternative is to insert a "fake" directory into sys.path as suggested by how to add a package to sys path for testing

    import os, sys
    fake_dir = os.path.join(os.path.dirname(__file__), 'fake')
    assert(os.path.exists(fake_dir))
    sys.path.insert(0, fake_dir)
    import foo
    from unittest.mock import sentinel
    def foo_test():
        foo.helpers.helper_func.return_value = sentinel.foobar
        assert foo.foo_func() == sentinel.foobar
    

    where fake is structured as:

    .
    ├── fake/
    │   └── helpers/
    │       └── __init__.py
    ├── foo.py
    └── helpers/
    

    and __init__.py has

    from unittest.mock import Mock
    helper_func = Mock()
    
    0 讨论(0)
  • 2021-02-19 01:54

    You're kind of missing the point of what a Mock is. You're supposed to build them when you want an object with a particular interface, regardless of how it's implemented.

    What you're doing is trying to re-implement python's module system, plus it's a pretty horrible abuse of global variables to boot.

    Instead of making foo a module, make a Foo class, and pass in the helpers in the constructor.

    class Foo(object):
        def __init__(self, helpers):
            self.helpers = helpers
    
    # then, instead of import foo:
    foo = Foo(mock_helpers)
    

    Even if the real "helpers" is actually going to be a module, there is no reason you need to be messing with sys.modules and setting it up via import in your tests.

    And if foo has to be a module, that's fine too, but you do it like this:

    # foo.py
    class Foo(object):
        pass # same code as before, plus foo_func
    
    try:
       import whatever
       _singleton = Foo(whatever)
    except ImportError:
       _singleton = Foo(something_else)
    
    def foo_func():
       return _singleton.foo_func()
    

    Large chunks of the standard library work this way. It's pretty much the standard for defining singleton-like modules.

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