Objects behaviour inheritance with RSpec

ADSENSE HERE!
About half of a year ago I was writing about object interface and Liskov Substitution Princeple. In short: Any class instance that extends the base class should pass all unit tests behaviour tests written for base class instance. It was a surprise for me that this concept has already been implemented in RSpec.



My previous article was primary inspired by Java programming language and it's interface concept. Unlike Java, Ruby does not have interfaces, but behaviour inheritance is still actual for both languages. RSpec seems the first testing framework that provide the ability to validate LSP and behavior inheritance with 'it_should_behave_like'.
With Ruby modules(mixins) feature we can build reusable code and include it in different classes(read more). With RSpec we can bundle the tests as well.



Let's review the following module that uses one of the Rails callback and adds some logging:


module LoggedModel
def after_save
super
handle_logging
end
end


and the some tests group for this module:

describe LoggableModel
it "should be loggable" do
LoggableModel.should ...
end
end


Now, we have a tested code that is going to be used in many cases like this:
class MyModel
include LoggableModel
def after_save
do_some_other_thing
end
end


OK, let's see what we have: after_save in MyModel overwrites after_save in LoggableModel and breaks the logging. This is simplest example when the behavior inheritance may be broken. Rspec shared examples groups allows you to ensure that the code in LoggableModel is used correctly from any inherited class. Let's change the definithin of LoggableModel tests.

shared_examples_for "logged model" do
it "should be loggable" do
subject.should...
end
end


'Subject' is the ultimate RSpec magic that let us make a simple abstraction with the tested class and reuse these shared examples in MyModel spec:

describe MyModel do
it_should_behave_like 'loggable model'
end


In this way we will rerun the LoggableModel examples for MyModel and make sure that it's behavior wasn't broken.
ADSENSE HERE!

4 comments:

  1. I've been doing this for years and years through simple test class inheritance.

    ReplyDelete
  2. Yes, RSpec's text classes are (AFAIK) somewhat opaque, so inheritance isn't as useful.

    ReplyDelete
  3. Shoulda allows similar stuff via merge_block method.

    ReplyDelete
  4. It is taken me a not much time to read all of the comments, but I seriously loved the article. I'm certain it's going to be very helpful to me. It's always an enjoyable surprise every time a post is both informative and enjoyable! Thanks :)

    ReplyDelete

Komen dong, tapi yang sopan dan tidak spam ya

Copyright © Spesial Unik. All rights reserved. Template by CB. Theme Framework: Responsive Design