Skip to content

Instantly share code, notes, and snippets.

@jeffh
Created October 18, 2011 19:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jeffh/1296365 to your computer and use it in GitHub Desktop.
Save jeffh/1296365 to your computer and use it in GitHub Desktop.
Python Micro-testing Framework. Inspired from py.test
# paste code here at bottom of any test file. Then run that python file.
# Provides primitive mocking and assertion behavior with some decent
# debugging output.
#
# Designed for use in homework allow sharing test cases with others
# (any not require them to install pip and py.test).
#
# simply prefix test functions with test and use the assert keyword
# for all test assertions.
#
# api inspiration from py.test and python-mock.
#
# tested only on python3k.
# see example: https://gist.github.com/1296369
from io import StringIO
import sys
import re
import traceback
from itertools import zip_longest
from pprint import pformat
class Mockify(object):
NONE = object()
def __init__(self, module, name, return_value=NONE, side_effect=NONE):
self._mod, self._name = module, name
self._return_value = None
self._return_value_was_set = False
self._side_effect = None
self._side_effect_was_set = False
if return_value != self.NONE:
self.return_value = return_value
if side_effect != self.NONE:
self.side_effect = side_effect
@property
def return_value(self):
return self._return_value
@return_value.setter
def return_value(self, value):
self._return_value = value
self._return_value_was_set = True
@property
def side_effect(self):
return self._side_effect
@side_effect.setter
def side_effect(self, value):
self._side_effect = value
self._side_effect_was_set = True
def __call__(self, *args, **kwargs):
if self._side_effect_was_set:
return self._side_effect(*args, **kwargs)
if self._return_value_was_set:
return self.return_value
raise TypeError("Mockify requires side_effect or return_value to be set")
def __enter__(self):
self._old = getattr(self._mod, self._name)
setattr(self._mod, self._name, self)
return self
def __exit__(self, type, value, traceback):
setattr(self._mod, self._name, self._old)
def most_recent_tb(tb):
prev_tb = tb
while prev_tb.tb_next:
prev_tb = prev_tb.tb_next
return prev_tb
PARTS = re.compile(r'\W([!=]=|not\W+in|in|[><]=?|or|and)\W')
def extract_parts(assert_line):
code = assert_line[len('assert'):].strip()
parts = PARTS.split(code)
if len(parts) > 1:
return [p for i, p in enumerate(parts) if i % 2 == 0], [p for i, p in enumerate(parts) if i % 2 == 1]
return [code], []
def re_eval(frame):
code = traceback.extract_stack(frame)[-1][-1]
parts, ops = extract_parts(code)
evaled = []
all_globals = {}
all_globals.update(frame.f_builtins)
all_globals.update(frame.f_globals)
for p in parts:
evaled.append(eval(p, all_globals, frame.f_locals))
return parts, evaled, ops
def format_testname(func):
return func.__doc__ or func.__name__[len('test_'):].replace('_', ' ')
def is_test(name, value):
return name.lower().startswith('test')
def tests_from_dict(vars):
"Returns all test from a given dictionary."
tests = []
for name, value in tuple(vars.items()):
if name.startswith('test_'):
tests.append(value)
return tests
def run_tests(tests, fail_fast=False):
print("Running {0} Tests:".format(len(tests)))
errors = {}
true_stdout = sys.stdout
for test in tests:
stdout = StringIO()
sys.stdout = stdout
try:
test()
true_stdout.write('.')
true_stdout.flush()
except Exception as e:
tb = most_recent_tb(sys.exc_info()[2])
snippets, values, ops = re_eval(tb.tb_frame)
true_stdout.write('F')
true_stdout.flush()
errors[test] = (traceback.format_exc(), stdout.getvalue(), zip_longest(ops, values, fillvalue=''))
if fail_fast:
break
sys.stdout = true_stdout
if not errors:
print("\n\nNo Errors ^_^")
sys.exit(0)
print("\n")
for test_name, (exc, stdout, tree) in errors.items():
print("----- {0} - FAILED -----\n\n{1}\n{2}\n".format(
format_testname(test_name),
exc,
'\n'.join(['%s\n%s' % (pformat(val, indent=4), op.strip()) for op, val in tree]),
))
if stdout:
print(":::STDOUT:::\n{0}".format(stdout))
print("==== End Errors ====")
sys.exit(1)
if __name__ == '__main__':
run_tests(tests_from_dict(globals()), fail_fast=('-f' in sys.argv or '--failfast' in sys.argv))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment