14

I am working on software for a robot, which is normally run on the Raspberry Pi. Let's consider the imports of two files:

motor.py (runs the motors):

from RPi import GPIO as gpio

and client.py (communicates with the server and relays commands to the motors):

from rpi.motor import Motor

Both files are in a directory called rpi, which contains a __init__.py and a __main__.py. The RPi package cannot be installed on non-RPi devices. However, I still want to test the functionality of client.py.

import unittest
from unittest import mock
# Location A


class TestClient(unittest.TestCase):
    # Location B
    setUp(self):
         # Location C
         pass

Originally, I tried from rpi.client import Client at LocA, but that failed because it tried to import Motor, and then import GPIO from RPi, which doesn't exist. I also tried mock.patch("rpi.client.Motor") at LocB (including adding mock_motor after self, and imported Client at LocC, but that failed as well. I tried mocking RPi at LocA, too, but it didn't work either.

How do you mock out a library that is not installed on your system?

Jean Nassar
  • 555
  • 1
  • 5
  • 13

1 Answers1

12

You can use patch.dict() to patch sys.modules and mock RPi module as showed in pointed documentation.

Use follow code at the top of your test module:

>>> from mock import MagicMock, patch
>>> mymodule = MagicMock()
>>> patch.dict("sys.modules", RPi=mymodule).start()
>>> from RPi import GPIO as gpio
>>> gpio
<MagicMock name='mock.GPIO' id='139664555819920'>
>>> import os
>>> os
<module 'os' from '/usr/lib/python2.7/os.pyc'>

In Python3 you have same behavior.


In your specific case use patch.dict is little bit overkill; maybe you aren't interested in patch context and original state recover. So you can simplify it by set sys.modules["RPi"] directly:

>>> from unittest.mock import MagicMock
>>> mymodule = MagicMock()
>>> import sys
>>> sys.modules["RPi"] = mymodule
>>> from RPi import GPIO as gpio
>>> gpio
<MagicMock name='mock.GPIO' id='140511459454648'>
>>> import os
>>> os
<module 'os' from '/usr/lib/python3.4/os.py'>
Michele d'Amico
  • 22,111
  • 8
  • 69
  • 76
  • I've verified that is a correct solution. To make the answer better I would expand on sys.modules. – Dan Sep 13 '15 at 05:23
  • As you can read at https://docs.python.org/3/library/unittest.mock.html#patch-dict `patch.dict()` default behavior preserve original dictionary and just inject new values. Anyway I integrated my answer with some other notes to make it more clear. – Michele d'Amico Sep 13 '15 at 10:01