Interface Tests in Ruby
Recently I needed to implement an api on two model classes in Ruby. The models were two fairly different animals, we'll call them Goose and Duck, that needed to be able to respond to certain method calls: walk, fly and swim. Java uses an Interface to implement an api like this. Ruby doesn't have, or even need, a similar construct. In Ruby, you simply implement methods with the same name in both classes and voila, they both implement the api. Ruby allows you to have a collection of subtypes and doesn't require you to specify a supertype. According to the Liskov Substitution Principle (LSP), objects of these subtypes should be interchangeable with one another.
After completing these changes in my application, I still felt a bit uncomfortable. What happens to the next hapless developer who joins the team and works on the Goose class and decides to refactor a method name from "walk" to "ambulate"? The refactoring will update the unit test method call and everything will pass, even through the api has been broken and the objects no longer adhere to the LSP. In Java, an Interface enforces the api and this change wouldn't compile. I needed a way to have the same type of enforcement capability in Ruby. But how?
A test, of course!
I already had unit tests in both models to exercise the implementations of all the api methods. What I needed was a different type of test. It needs to make sure the api methods of the implementing follow the api and conform to the LSP. This test doesn't belong to either of the implementing classes, so I created a new .rb file for it. The idea is to setup an instance of each implementing class and use the built in "respond_to?" method to ensure the api method exists on each model class. The code is below.
def test_waterfowl_contract
duck = Duck.create
goose = Goose.create
%w(walk fly swim).each do |method|
assert duck.respond_to?(method.to_sym)
assert goose.respond_to?(method.to_sym)
end
end