How do I mock the hierarchy of non-existing module

2020-07-08 07:11发布

问题:

Let's assume that we have a system of modules that exists only on production stage. At the moment of testing these modules do not exist. But still I would like to write tests for the code that uses those modules. Let's also assume that I know how to mock all the necessary objects from those modules. The question is: how do I conveniently add module stubs into current hierarchy?

Here is a small example. The functionality I want to test is placed in a file called actual.py:

actual.py:


def coolfunc():
  from level1.level2.level3_1 import thing1
  from level1.level2.level3_2 import thing2
  do_something(thing1)
  do_something_else(thing2)

In my test suite I already have everything I need: I have thing1_mock and thing2_mock. Also I have a testing function. What I need is to add level1.level2... into current module system. Like this:

tests.py

import sys
import actual

class SomeTestCase(TestCase):
  thing1_mock = mock1()
  thing2_mock = mock2()

  def setUp(self):
    sys.modules['level1'] = what should I do here?

  @patch('level1.level2.level3_1.thing1', thing1_mock)
  @patch('level1.level2.level3_1.thing1', thing2_mock)
  def test_some_case(self):
    actual.coolfunc()

I know that I can substitute sys.modules['level1'] with an object containing another object and so on. But it seems like a lot of code for me. I assume that there must be much simpler and prettier solution. I just cannot find it.

回答1:

So, no one helped me with my problem and I decided to solve it by myself. Here is a micro-lib called surrogate which allows one to create stubs for non-existing modules.

Lib can be used with mock like this:

from surrogate import surrogate
from mock import patch

@surrogate('this.module.doesnt.exist')
@patch('this.module.doesnt.exist', whatever)
def test_something():
    from this.module.doesnt import exist
    do_something()

Firstly @surrogate decorator creates stubs for non-existing modules, then @patch decorator can alter them. Just as @patch, @surrogate decorators can be used "in plural", thus stubbing more than one module path. All stubs exist only at the lifetime of decorated function.

If anyone gets any use of this lib, that would be great :)



回答2:

You can use the "create" parameter in the patch() method that will force to create the attribute if it doesn't exists.