This post contains some python-related information, I promise.
Fun time. Corey Haines explained behavior-driven development and showed some examples using RSpec at last night’s Cleveland Ruby meetup.
As an aside, Corey said “powershell is what the unix command line will be when it grows up” and a thousand angels fell over dead when they heard this blasphemy.
The story-based tests in RSpec seem downright magic. You can write in an english-y syntax:
Given a = 1,
When
b.foo(a)
Then
b should return "Hurray"
Or something like that.
I like that RSpec supports a result called “Pending”. This guy writes a good explanation of how it works, and I agree with this remark:
It’s easy enough to rename a test method so it doesn’t execute, but before RSpec I’ve never worked with one where you can mark it as pending and it then reminds you that you still have work to come back too.
I figure that it would be straightforward to add this into nose. Maybe raise a special exception called PendingTest that gets caught differently.
I learned a neat way of using a mock object without having to pass it in as a parameter based on some code I saw last night.
Corey had a couponcontroller that operated on coupon objects. He made a mock coupon object to use with his tests for his couponcontroller. Then, in his test code, he monkeypatched the coupon module so that when somebody said “give me a coupon” he got a mock coupon instead.
I spent a few minutes trying something vaguely like that in python. I’m not sure I like it, but it gets the point across.
I have a file coupon.py:
# This is coupon.py.
class Coupon(object):
"I'm the real coupon"
def foo(self):
print "This is the real coupon"
return "foo"
And I have a file couponcontroller.py:
# This is couponcontroller.py.
from coupon import Coupon
def couponcontroller():
c = Coupon()
return c.foo()
In my test_couponcontroller.py, I want the couponcontroller to use my mock coupon, not the real one.
# This is test_couponcontroller.py.
import couponcontroller
class mockCoupon(object):
"I'm not the real coupon."
def foo(self):
print "Congratulations. You're using a mock."
return "foo"
def setup():
# Mess with the module.
couponcontroller.Coupon = mockCoupon
def test_couponcontroller():
"couponcontroller should return a string 'foo'"
assert couponcontroller.couponcontroller() == "foo"
It seems to work:
$ nosetests -s test_couponcontroller.py
couponcontroller should return a string 'foo' ... Congratulations. You're using a mock.
ok
----------------------------------------------------------------------
Ran 1 test in 0.003s
OK
In summary, there’s clearly a lot of smart people in the ruby community, even if they insist on using syntax like
@adder ||= Adder.new