2

I have a python file the imports certain modules that aren't easily testable on all systems. Just importing the module on macOS for example causes an exception. Is there a way to mock this. Essentially I want to change the source code on one line before actually running the import statement.

Test code

from module_a import func

def test_func():
    print("...")

Module code

module_a.py

import os
import numpy as np
# ...
import magicmodulethatbreaksonmac

def func():
   # Nothing inside this function needs magicmodulethatbreaksonmac 

Would be great if in the test code, I could do some sort of mock on module_a.py to not actually try to import magicmodulethatbreaksonmac but rather just don't import anything as it's not needed to test this function.

Is there a way to put something of the same name in the namespace already that doesn't have anything in it so when it tries to import it again here, nothing happens? (If that is the behavior...)

I would think there's something here, but I'm not seeing it: https://docs.python.org/3/library/unittest.mock.html

SwimBikeRun
  • 4,192
  • 11
  • 49
  • 85
  • The main issue here is that your test's imports force an evaluation on your `module_a`, which then does an import of `magicmodulethatbreaksonmac`. First thing you'll have to do is move that `from module_a import func` inside your test. Then maybe something like the answers here right before your `func` import: https://stackoverflow.com/questions/8658043/how-to-mock-an-import. I'm more curious what's breaking inside that module though. It might be worth mocking what's going wrong in there instead. – wholevinski Jul 30 '19 at 11:41
  • A bunch of things, including tkinter dependencies and other things that don't translate perfectly between mac and linux. Mocking them could also be an option, but it's the same exact problem, how to mock bypass something that will be imported? – SwimBikeRun Jul 30 '19 at 16:03
  • Hm, alright. Maybe try the `sys.modules` approach from that link I tried and move the `from module_a import func` into your test function. – wholevinski Jul 30 '19 at 16:11
  • Yes the link has been pretty helpful. I am still playing with it, but I can update with what worked and give you credit as the answer in a bit if you'd like – SwimBikeRun Jul 30 '19 at 19:27
  • Sorry, I had posted a solution but noticed a glaring error in it and didn't have time to edit it to fix it. Just un-deleted my POC for how I envisioned it working. Hope everything worked out! – wholevinski Jul 31 '19 at 10:36

1 Answers1

2

Here's sort of what I'm proposing with the sys.modules solution. You have options as far as if you want to do that mock globally, in each test case:

'''bad_module.py'''
raise RuntimeError("don't import me")

'''my_module.py'''
from bad_module import somestuff


def my_func():
    pass

'''test_my_module.py'''
import sys
import unittest

from unittest import mock

def test_stuff():
    sys.modules['bad_module'] = mock.Mock()
    from my_module import my_func
    my_func()

If I don't mock I get this:

...
  File "/home/wholevinski/so_test/mock_import/test_my_module.py", line 2, in <module>
    from my_module import my_func
  File "/home/wholevinski/so_test/mock_import/my_module.py", line 1, in <module>
    from bad_module import somestuff
  File "/home/wholevinski/so_test/mock_import/bad_module.py", line 1, in <module>
    raise RuntimeError("don't import me")
RuntimeError: don't import me

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (errors=1)

With the mock I get this:

(p36) [localhost mock_import]$ nosetests test_my_module.py 
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK
wholevinski
  • 3,658
  • 17
  • 23