2

I've written a Python program that makes use of the resource module on Unix systems to do some timing operations, but falls back to using the time module on Windows (since the resource module is unavailable). The code looks something along the lines of this:

try:
    import resource
except ImportError:
    import time
    def fn():
        # Implementation using the time module
else:
    def fn():
        # Implementation using the resource module

However, my tests currently only ever execute function in the else block, since the resource module is always available on Linux. Is there a way to simulate the unavailability of built-in modules such that I can test the time-based function as well? I've looked through the mock documentation but couldn't find anything about this specifically.

Kevin Yap
  • 670
  • 10
  • 22
  • To put it clearly, you want to simulate *the unavailability* of built-in modules, correct? – jedwards Mar 15 '15 at 06:37
  • Right — good point, I'll edit the title. – Kevin Yap Mar 15 '15 at 06:38
  • Good catch on the unavailability vs inavailability -- I was trying to edit my comment before you made my mistake -- despite apparently being incorrect, inavailability does sound more appropriate IMO :-P – jedwards Mar 15 '15 at 06:40
  • 1
    If you are trying to test external dependencies, then wouldn't you be looking for an **integration test** rather than a unit test? – Tim Biegeleisen Mar 15 '15 at 06:45
  • @TimBiegeleisen Ah, thanks for the terminology correction; it appears I got myself mixed up whilst adding the `unittest` tag. – Kevin Yap Mar 15 '15 at 06:48

1 Answers1

2

It's a bit messy but you could make a directory with a file called resource.py that raises an ImportError, and prepend the path to that directory to sys.path.

If you want to mix mocked tests with unmocked tests you'll also have to mess around with removing the module from sys.modules when you want to retry importing it, otherwise the changes to sys.path will have no effect. There could be further issues with submodules if there are any.

Proof of concept:

mocked_modules/resource.py:

raise ImportError()

Test code:

import sys
import os

original_path = sys.path[:]
def reset():
    # Teardown
    sys.path = original_path[:]
    if "resource" in sys.modules:
        del sys.modules["resource"]


import resource
print("resource exists")

reset()

sys.path.insert(0, os.path.join(os.getcwd(), "mocked_modules"))


try:
    import resource
except ImportError:
    print("resource does not exist")

reset()
import resource
print("resource exists")

Output:

resource exists
resource does not exist
resource exists

This makes me feel kinda uneasy given the number of caveats it has (cf. this question), so if it's your code doing the import you might prefer to just abstract the import into a function and mock that instead. Alternatively or in addition you could expose the "Implementation using the time module" and test it directly.

Community
  • 1
  • 1
Andrew Magee
  • 6,506
  • 4
  • 35
  • 58
  • Hmm, I had a feeling that any solutions would be sort of messy like this. I'll probably do what you mentioned in the final paragraph (exposing the function and testing it directly). I'll leave this question unaccepted for a bit in case any other answers come along. – Kevin Yap Mar 15 '15 at 18:44