Skip to content

Instantly share code, notes, and snippets.

@spiralman
Created April 7, 2013 18:35
Show Gist options
  • Save spiralman/5331815 to your computer and use it in GitHub Desktop.
Save spiralman/5331815 to your computer and use it in GitHub Desktop.
Mocha inspired, BDD style testing for Python, using decorators.
import unittest
import collections
tests = collections.defaultdict(list)
class BddLoader(object):
def loadTestsFromModule(self, module):
return unittest.TestSuite(tests[module.__name__])
class Context(object):
def __init__(self, name, outer=None):
self.outer = outer
self.name = name
self.beforeEachCallback = None
self.afterEachCallback = None
def full_name(self):
names = [self.name]
outer = self.outer
while outer is not None:
names.insert(0, outer.name)
outer = outer.outer
return ' '.join(names)
def setUp(self, testCase):
if self.outer is not None:
self.outer.setUp(testCase)
if callable(self.beforeEachCallback):
self.beforeEachCallback(testCase)
def tearDown(self, testCase):
if callable(self.afterEachCallback):
self.afterEachCallback(testCase)
if self.outer is not None:
self.outer.tearDown(testCase)
def __call__(self, f):
"Allows the Context object to be used as the 'given' decorator"
f.__globals__.update(
{
'given': self.given,
'beforeEach': self.beforeEach,
'afterEach': self.afterEach,
'it': self.it
})
f()
return self
### The following are all decorators ###
def given(self, name):
return Context(name, self)
def beforeEach(self, f):
self.beforeEachCallback = f
return f
def afterEach(self, f):
self.afterEachCallback = f
return f
def it(self, name):
def decorate(f):
tests[f.__module__].append(Expectation(self, name, f))
return f
return decorate
given = Context
class Expectation(unittest.TestCase):
def __init__(self, context, name, function):
super(Expectation, self).__init__()
self.context = context
self.name = name
self.function = function
def setUp(self):
self.context.setUp(self)
def tearDown(self):
self.context.tearDown(self)
def runTest(self):
self.function(self)
def shortDescription(self):
return self.name
def __str__(self):
return self.context.full_name()
import unittest
from bdd import given, BddLoader
@given('A number')
def context():
@beforeEach
def setUp(self):
self.x = 5
@it('should be able to be converted to a string')
def test(self):
self.assertEqual(str(self.x), '5')
@it('should be 7')
def test(self):
self.assertEqual(self.x, 7)
@given('with another number')
def context():
@beforeEach
def setUp(self):
self.y = 3
@it('should add up to 8')
def test(self):
self.assertEqual(self.x + self.y, 8)
@it('should reduce to 1')
def test(self):
self.assertEqual(self.x - self.y, 1)
@given('a string')
def context():
@beforeEach
def setUp(self):
self.s = 'hello'
@it('should equal "hello"')
def test(self):
self.assertEqual(self.s, 'hello')
if __name__ == '__main__':
unittest.main(testLoader=BddLoader())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment