Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking a module import in pytest

Tags:

I am writing a pytest plugin that should test software that's designed to work inside a set of specific environments.

The software I'm writing is run inside a bigger framework, which makes certain Python modules available only when running my Python software inside the framework.

In order to test my software, I'm required to "mock" or fake an entire module (actually, quite a few). I'll need to implement its functionality in some kind of similar-looking way, but my question is how should I make this fake Python module available to my software's code, using a py.test plugin?

For example, let's assume I have the following code in one of my source files:

import fwlib  def fw_sum(a, b):     return fwlib.sum(a, b) 

However, the fwlib module is only made available by the framework I run my software from, and I cannot test inside it.

How would I make sure, from within a pytest plugin, that a module named fwlib is already defined in sys.modules? Granted, I'll need to implement fwlib.sum myself. I'm looking for recommendations on how to do just that.

like image 962
NirIzr Avatar asked Apr 01 '17 22:04

NirIzr


People also ask

Can you mock an import in Python?

The Python Import Mocker provides an easy way to import a module and mock its dependencies in an isolated way.

Can you use mock with Pytest?

In pytest , mocking can replace the return value of a function within a function. This is useful for testing the desired function and replacing the return value of a nested function within that desired function we are testing.

How do you mock an import jest?

To mock an imported function with Jest we use the jest. mock() function. jest. mock() is called with one required argument - the import path of the module we're mocking.


1 Answers

pytest provides a fixture for this use-case: monkeypatch.syspath_prepend.

You may prepend a path to sys.path list of import locations. Write a fake fwlib.py and include it in your tests, appending the directory as necessary. Like the other test modules, it needn't be included with the distribution.

After playing with this myself, I couldn't actually figure out how to get the fixture to mock module level imports correctly from the library code. By the time the tests run, the library code was already imported and then it is too late to patch.

However, I can offer a different solution that works: you may inject the name from within conftest.py, which gets imported first. The subsequent import statement within the code under test will just re-use the object already present in sys.modules.

Package structure:

$ tree . . ├── conftest.py ├── lib │   └── my_lib.py └── tests     └── test_my_lib.py  2 directories, 3 files 

Contents of files:

# conftest.py import sys  def fwlib_sum(a, b):     return a + b  module = type(sys)('fwlib') module.sum = fwlib_sum sys.modules['fwlib'] = module 

library file:

# lib/my_lib.py import fwlib  def fw_sum(a, b):     return fwlib.sum(a, b) 

test file:

# lib/test_my_lib.py import my_lib  def test_sum():     assert my_lib.fw_sum(1, 2) == 3 
like image 130
wim Avatar answered Sep 20 '22 06:09

wim