2018年2月8日 星期四

[ Python 文章收集 ] mock - Mocking Python imports

Source From Here 
Preface 
Writing unit tests with Python is a joy, especially with the excellent mock library. You can tweak the language and mock almost anything to your will, making testing even the smallest of units very easy. HOWEVER , mocking imports, when a class / module depends on imports which you might not have on your machine, such as windows modules (oei vei) when you are (and you should be) on a nix machine. 

Another typical case is when you integrate a module to a big system, which imports THE ENTIRE INTERNETZ in each file. In those cases it’s critical to be able to isolate your class / module by totally disconnecting it from those modules. 

Mock to the rescue 
Let's check the file structure for demonstration: 
# tree ./ 
./ 
├── my_module.py 
├── __pycache__ 
│   ├── my_module.cpython-35.pyc 
│   ├── test_bad_module.cpython-35-PYTEST.pyc 
│   └── third_party_module.cpython-35.pyc 
├── test_bad_module.py 
└── third_party_module.py

- third_party_module.py 
  1. from the.internetz import everything  
  2. import logging  
  3.   
  4. class World(object):  
  5.     """  
  6.     what a grand class  
  7.     """  
  8.   
  9.     def say(self, msg):  
  10.         """  
  11.         It speaks!  
  12.         """  
  13.         everything.log(msg)  
  14.         print("Say: {}".format(msg))  
- my_module.py 
  1. from third_party_module import World  
  2.   
  3. class MyModule(object):  
  4.     """  
  5.     My shining class  
  6.     """  
  7.   
  8.     def __init__(self):  
  9.         """  
  10.         Let's instanciate the World (muhaha)  
  11.         """  
  12.         self.world = World()  
  13.   
  14.     def hello(self):  
  15.         """  
  16.         My shining method  
  17.         """  
  18.         self.world.say("hello")  
- test_bad_module.py 
  1. from unittest import TestCase  
  2. from mock import MagicMock  
  3.   
  4. from my_module import MyModule  
  5.   
  6. class TestBadMyModule(TestCase):  
  7.     """  
  8.     Let's test my module!  
  9.     """  
  10.   
  11.     def test_hello_should_say_waitforit_hello(self):  
  12.         """  
  13.         We want to test that the hello method calls the say function with the string "hello:  
  14.         """  
  15.   
  16.         m = MyModule()  
  17.         m.world = MagicMock()  
  18.         m.hello()  
  19.   
  20.         m.world.say.assert_called_once_with("hello")  
Launch the testing will get ImportError: 
# pytest test_bad_module.py 
... 
ImportError while importing test module '/root/tmp/Blogdemo/test_bad_module.py'. 
Hint: make sure your test modules/packages have valid Python names. 
Traceback: 
test_bad_module.py:4: in  
from my_module import MyModule 
my_module.py:1: in  
from third_party_module import World 
third_party_module.py:1: in  
from the.internetz import everything 
E ImportError: No module named 'the'

To fix this, we need to mock the “the internetz” module: 
- test_bad_module.py (Updated) 
  1. from unittest import TestCase  
  2. from mock import MagicMock, patch  
  3.   
  4. class TestBadMyModule(TestCase):  
  5.     """  
  6.     Let's test my module!  
  7.     """  
  8.     def setUp(self):  
  9.         """  
  10.         It's patching time  
  11.         """  
  12.         self.internetz_mock = MagicMock()  
  13.         self.internetz_mock.the.internetz.everything.log.return_value = True  
  14.         modules = {  
  15.             'the': self.internetz_mock,  
  16.             'the.internetz': self.internetz_mock.internetz,  
  17.         }  
  18.   
  19.         self.module_patcher = patch.dict('sys.modules', modules)  
  20.         self.module_patcher.start()  
  21.         from my_module import MyModule  
  22.         self.my_module = MyModule  
  23.   
  24.     def tearDown(self):  
  25.         """  
  26.         Let's clean up  
  27.         """  
  28.         self.module_patcher.stop()  
  29.   
  30.     def test_hello_should_say_waitforit_hello(self):  
  31.         """  
  32.         We want to test that the hello method calls the say function with the string "hello:  
  33.         """  
  34.   
  35.         m = self.my_module()  
  36.         m.world = MagicMock()  
  37.         m.hello()  
  38.   
  39.         m.world.say.assert_called_once_with("hello")  
The reason to put all the patches, mocks and imports in the setUp function is that you’d probably reuse them in the same test class on other test methods. 

This time to launch the testing will pass: 
# pytest test_bad_module.py 
====================================== test session starts ====================================== 
platform linux -- Python 3.5.0, pytest-3.2.1, py-1.4.34, pluggy-0.4.0 
rootdir: /root/tmp/Blogdemo, inifile: 
plugins: celery-4.1.0 
collected 1 item 

test_bad_module.py . 

=================================== 1 passed in 0.07 seconds


沒有留言:

張貼留言

[ Py DS ] Ch1 - IPython: Beyond Normal Python

Source From  Here   Keyboard Shortcuts in the IPython Shell   If you spend any amount of time on the computer, you’ve probably found a u...