Tuesday, December 25, 2007

Ruby: expectations gem

In February I wrote about removing test noise. 10 months later, I finally took the time to write the unit testing framework I've been wanting for the past year: expectations

expectations is a lightweight unit testing framework. Tests (expectations) can be written as follows
  expect 2 do
1 + 1
end

expect NoMethodError do
Object.invalid_method_call
end.
expectations is designed to encourage unit testing best practices such as
  • discourage setting more than one expectation at a time
  • promote maintainability by not providing a setup or teardown method
  • provide one syntax for setting up state based or behavior based expectation
  • focus on readability by providing no mechanism for describing an expectation other than the code in the expectation. Since there is no description, hopefully the programmer will be encouraged to write the most readable code possible.
A few things probably come to mind right away: sometimes setup is good, sometimes a description is a good thing, sometimes creating an object is so painful that I need to be able to add multiple assertions. All of those things are true, but generally I don't run into those situations while unit testing. Those situations generally pop up with functional testing, and while functional testing you are better off using RSpec or Test::Unit.

The more I write tests the more I believe that there doesn't need to be a silver bullet testing framework. I think expectations is a good solution for unit testing, and I think RSpec or Test::Unit are good solutions for functional testing, and I plan to use both expectations and RSpec on all my projects moving forward.

I'm currently testing several of my projects using expectations, so I do believe it's in decent shape for using, but it is still very new. As always, patches are welcome.

Mocking is done using Mocha

Here's a few more examples of how easy it is to test with expectations

Expectations do

# State based expectation where a value equals another value
expect 2 do
1 + 1
end

# State based expectation where an exception is expected. Simply expect the Class of the intended exception
expect NoMethodError do
Object.no_method
end

# Behavior based test using a traditional mock
expect mock.to.receive(:dial).with("2125551212").times(2) do |phone|
phone.dial("2125551212")
phone.dial("2125551212")
end

# Behavior based test on a concrete mock
expect Object.to.receive(:deal).with(1) do
Object.deal(1)
end

end
Post a Comment