RVM + rSpec = Compatibility Fun!
Rubyists make a decision which version of which flavour is their preferred, either generally or for that project. But, does it not seem that sitting inside their chosen one a little closed-minded and unfriendly to others’ preferences? It does to me, so I’m combining the magic of RVM with my rSpec testing to make my software cross-version — and even cross-Ruby — compatible.
Erm… Ahem.
Before we go any further I need to check that you took my advice and integrated RVM with your Ruby projects, yeah? Good, I hope it’s been a good experience for you to chop up your development environment into per-project workspaces; if you haven’t, GTFO.
Great: you guys and girls left are the ones I want to deal with. Now you must prepare for your next challenge: combining RVM with that ever-so-awesome testing tool rSpec. Don’t worry if this seems over-the-top for your project; it’s worthy practice in even the most modest of code-bases. I’ve only the smallest of personal projects and only one I deem worthy of such a practice. Coming to the conclusion that I’d like my rubident code to run in more than ten minutes time, I have to make sure it’s going to work the second, third and hundredth time round. How? Test it!
The Project
Embarking on rubident out-of-the-blue meant I hadn’t looked into how a Ruby project could consume the web services and APIs it does so I just went ahead and wrote some code. Yep, I got myself into development-driven testing; I wanted to find my feet in how best to organise it before fighting the dreaded red-coloured output since I didn’t know rSpec at the time either! Around about here rubident looked like this:
- OOP, not just a script as it once was (turns out doing it the proper way is just as simple!)
- set multi-service support
- any number of accounts on those services
- oAuth (v1) authentication through them
- reading and writing pretty much all the expected streams; home, replies, direct messages etc.
- a couple of helpers to sort out the dates
- hosting on Gitorious and GitHub.
Since the specifications for the project is to eventually support everything the API does (what more can a guy do?) the one thing missing from that list is the big one: tests. I had to get testing up there, so now that I have something to work with I can learn how Rubyists test!
Testing: And So It Begins
At the time of writing, the documentation for the newer rSpec (v2) lacks greatly from that of it’s predecessor, which is quite unsettling for a beginner. This made it quite difficult to pick up rSpec as I didn’t want to read documentation and tutorials for v1 and find out I’d need to re-write my attempts. Admitting defeat, I just wrote some ruddy code. I already had a list of features that rubident did:
- start a client instance
- make sure we load some gems (JSON et. al.)
- make sure we load some files (helpers)
- support a number of (technologically very similar) services
- add accounts on those service
- understand oAuth (ahem, I’m doing this one last…) to add those accounts
- read various timelines
- parse and order various stuff out of each stream
- post through an account
Writing this list made writing the list of tests easy:
- load ‘rubident.rb’
- it should create a client to work with
- it should load at least some of the required gems
- it should read a file of supported services
- it should read a file of set-up accounts
When starting up:
When setting up services and accounts:
I’m sure you can see where I’m going with this; adding this list of requirements as the rSpec file for my main rubident class — spec/rubident_spec.rb — gave me an instant template for testing my code. I’m not going to go into the actual code, hat’s up to you to make sure your tests actually do, but I will say that:
- it should create a client to work with
- it “should create a client to work with” do
@client = Rubident.new
@client.should_not be nil
end
The Awesome
Here’s the TL;DR of this post if you’re using RVM, rSpec and have written some code and tests. I’ve plumped for a plain ol’ Bash script to do the following:
- list all the Rubies installed on my system through RVM, in a machine-readable format (the strings bit)
- switch to the rubident environment for each installed Ruby and version
- ensure all my Gems are installed as per the Gemfile
- run my rSpec tests for this particular Ruby and version, coloured and listing my tests
- carry on to the next Ruby or version
- at the end, switch back to my preferred Ruby and version.
for RUBY in `rvm list strings` ; do
rvm use $RUBY@rubident && bundle install && rspec -cfn spec/*.rb ; echo
done
rvm use 1.9.2@rubident
This gives me 100% compatibility for all my installed Rubies as far as I have tested, in one command. By having older Rubies installed, I can maintain backward-compatibility too. Of course, this is limited to as much backward-compatibility as the gems allow for, and how far my tests go but I’ve found this is enough to inspire my confidence that I am writing well-working code
I immediately saw a failure resulting from changes between versions: MRI 1.8.7 can require both gems and files and load files, whereas v1.9.2 only require gems but still load files. By getting them green without version-specific code, one can ensure it is running at a sort of baseline, without specific features. You can also include optimisations for specific Rubies in your code and have them tested, since we are switching between whatever is installed — but that’s up to how you want to work.
Thanks for reading! I hope you find my post useful if not interesting, and appreciate any (appropriate!) comments.
By the way, I use the term Rubies to group not just all Ruby implementations but also every version of them. I thought it might be useful to clarify this. How do most use the term?



