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.