Commit 35647b98 by Ethan Gafford

Decorator and Mock Test

Intentional changes:
1) Added a decorator implementation of CodeBlockTimer.
2) Wrote a unit test for said decorator using a mock.Mock of
   the storage class.

Side effect changes:
1) Removed dependency on path module.
2) Changed storage import to module-level import (run-time module
   member lookups are helpful for safe patched mocking.)
3) Added test dbs to .gitignore.

Thanks; this was fun.
parent fc5e63a4
*.pyc *.pyc
code_block_timer/tests/tmp_db_*
...@@ -12,9 +12,11 @@ ...@@ -12,9 +12,11 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import functools
import threading import threading
from timeit import default_timer from timeit import default_timer
from .storage import TimingDataStorage
import storage
class Globals(threading.local): class Globals(threading.local):
...@@ -38,7 +40,7 @@ class CodeBlockTimer(object): ...@@ -38,7 +40,7 @@ class CodeBlockTimer(object):
self.block_desc = block_desc self.block_desc = block_desc
self.verbose = False if 'verbose' not in kwargs else kwargs['verbose'] self.verbose = False if 'verbose' not in kwargs else kwargs['verbose']
self.timer = default_timer self.timer = default_timer
self.data_store = TimingDataStorage(**kwargs) self.data_store = storage.TimingDataStorage(**kwargs)
def __enter__(self): def __enter__(self):
if len(_m.nest_stack) == 0: if len(_m.nest_stack) == 0:
...@@ -65,3 +67,13 @@ class CodeBlockTimer(object): ...@@ -65,3 +67,13 @@ class CodeBlockTimer(object):
print '{}: elapsed time: {} ms'.format( print '{}: elapsed time: {} ms'.format(
self.block_desc, self.elapsed self.block_desc, self.elapsed
) )
def code_block_timer(block_desc, **cbt_kwargs):
def outer(func):
@functools.wraps(func)
def inner(*args, **kwargs):
with CodeBlockTimer(block_desc, **cbt_kwargs):
return func(*args, **kwargs)
return inner
return outer
...@@ -15,16 +15,15 @@ ...@@ -15,16 +15,15 @@
import os import os
import sqlite3 import sqlite3
from path import path
MODULE_DIR = path(__file__).dirname() MODULE_DIR = os.path.dirname(__file__)
class TimingDataStorage(object): class TimingDataStorage(object):
SCHEMA_NAME = 'schema.sql' SCHEMA_NAME = 'schema.sql'
SCHEMA_PATH = MODULE_DIR / SCHEMA_NAME SCHEMA_PATH = '%s/%s' % (MODULE_DIR, SCHEMA_NAME)
DEFAULT_DB_NAME = 'block_times.db' DEFAULT_DB_NAME = 'block_times.db'
def __init__(self, **kwargs): def __init__(self, **kwargs):
......
import mock
import os import os
import unittest import unittest
import random import random
import sqlite3 import sqlite3
from code_block_timer import CodeBlockTimer, _m from code_block_timer import CodeBlockTimer, code_block_timer, _m
class TestCodeBlockTimer(unittest.TestCase): class TestCodeBlockTimer(unittest.TestCase):
...@@ -34,9 +35,31 @@ class TestCodeBlockTimer(unittest.TestCase): ...@@ -34,9 +35,31 @@ class TestCodeBlockTimer(unittest.TestCase):
z += i z += i
self._verifyEvents(run_id, ['test', ] + ["test:{}".format(x) for x in iterations]) self._verifyEvents(run_id, ['test', ] + ["test:{}".format(x) for x in iterations])
@mock.patch('code_block_timer.storage.TimingDataStorage')
def test_decorator(self, mock_class):
store = mock.Mock()
mock_class.return_value = store
store.run_id.return_value = 45
@code_block_timer('decorator_test', db_name=self.db_name)
def wrapped_thing(*args, **kwargs):
self.assertEquals(args, ('an_arg',))
self.assertEquals(kwargs, {'a_dict': {}})
kwargs['a_dict'].update(entered=True)
test_dict = {}
run_id = wrapped_thing('an_arg', a_dict=test_dict)
mock_class.assert_called_once_with(db_name=self.db_name)
self.assertTrue(test_dict['entered'])
store.store.assert_called_with(45, 'decorator_test', mock.ANY)
def tearDown(self): def tearDown(self):
# Destroy the sqlite DB. try:
os.remove(self.db_name) # Destroy the sqlite DB.
os.remove(self.db_name)
except:
pass
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment