An idea for a Python BDD-style spec runner inspired by rspec.
# the unit under test
def factorial(n):
if n < 0:
raise ValueError, "n must be non-negative"
else:
return n * factorial(n - 1) if n else 1# the spec
from spec import describe, done
with describe("The factorial function") as it:
with it("is defined at zero") as then:
then(factorial(0)).should.be(1)
with it("rejects a negative argument") as then:
then(lambda: factorial(-1)).should.throw(ValueError)
with it("yields 720 if input is 6") as then:
then(factorial(6)).should.be(720)
with it("grows fast") as then:
then(factorial(100)).should > 100000
done()
# ^ don't forget!Just run this with python and you will get the following output. Try to break a test case and re-run.
The factorial function
is defined at zero.
rejects a negative argument.
yields 720 if input is 6.
grows fast.
4 of 4 assertions passed.
Types of Expectations:
with describe("A Feature") as it:
with it("behaves this way") as then:
# should_not for denying any expectation:
then('abc').should_not.be('ABC')
then(lambda: 42 / 7).should_not.throw(ZeroDivisionError) # Comparison operators:
then(42).should > 23
then(42).should < 666
then(42).should >= 42
then(42).should <= 42 # Boolean statements
then(isinstance(42, int)).should.hold() # Duck Typing
then('a string').should.have('join') # Collection checking
then([1, 2, 3]).should.contain(1) # Expecting exceptions
then(lambda: 1 / 0).should.throw(ZeroDivisionError) # RegEx matching
then("a string").should.match("^a")Finalizing the test run:
done() prints the test summary and exits the interpreter returning the number of failed expectations as exit code.
done(exit=False) also prints the summary but does not exit. Anyhow, it resets the result collector including the counters for failed and succeeded tests.
spec_spec.py contains the tests for the test runner itself. It uses all features of the spec framework itself including custom expectations/assertions and a mocked result collector.