Without digressing into a discussion of object-oriented versus procedural programming, we can at least admit that “one size” does not fit all. In any event, use the paradigm which best applies to the problem you are trying to solve. With that said, in the cases where we want to write procedural code, how can we test it? With RSpec, this is not really much different than testing objects.
Ruby truly is a object-oriented language. Top-level methods are scoped as private methods to Object. All classes are children of Object, so adding methods to Object is semantically the same as defining a global method. Let’s take a look at the code we want to describe:
1 2 3 4 5 6 7 8 9 10 |
# procedural.rb def transform something end def validate something end def process something transform(something) if validate(something) end |
The actual implementation details for transform and validate aren’t the subject of this discussion. What we want to look at is how to test that process is doing what we expect.
- validate is called with something each time
- transform is called with something only if validate evaluates to true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
require "procedural" describe "process" do it "validates the data" do stub! :transform should_receive(:validate).with(:something) process :something end it "transforms the data when it validates" do stub!(:validate).and_return true should_receive(:transform).with(:something) process :something end it "does not transform invalid data" do stub!(:validate).and_return false should_not_receive :transform process :something end end |
What we see here is that syntactically, testing top level methods isn’t that much different than testing methods on objects. The key with RSpec is to understand that should_receive, stub! and other methods are available at the current scope level. The consequence of this functionality enables the tester to do some very interesting things such as stubbing out raise. Use this knowledge wisely.
I have linked this post at the Drink Rails blog.