Hey developers, I got something real important to give you. So just sit down, and listen.
We recently started a number of projects, each of which will require the use of a common library. So, here’s how it went down:
* Create the common library as a gem using the jeweler tool.
* Install Gem in a Box to allow the gem to be downloaded internally by our developers using gem install.
* Upload the common library gem.
BLAMMO! Now every developer can pull my gem ;)
But seriously, “Gem in a Box” is awesome and a great way to share gems internally at your office.
Let me be more explicit and say I’m going to be talking about Rails application packaging. Sorry, I needed a good post title for the lulz and the page views.
There are a few rake tasks that I’ve been using more and more now that all of our applications are running in a shared, virtual enviroment. They are:
Everyone should know and use this task at least once in their Rails-development life.
So, once I’ve created my project with “rails [project name]”, then next thing I do is “rake rails:freeze:gems” to freeze the Rails gems. I’ll enumerate the application’s dependencies in the environment.rb file and then run “rake gems:unpack” to make sure those dependencies exist with the application. Example:
config.gem 'will_paginate', :version => '2.3.11', :source => 'http://gemcutter.org' config.gem 'factory_girl', :version => '1.2.4', :source => 'http://gemcutter.org' config.gem 'fakeweb', :version => '1.2.8', :source => 'http://gemcutter.org'
Why do I like this approach? In a shared environment, it means we don’t have to have our systems folks install any dependencies for our application to run making the application self-contained. This is mostly fine for gems that don’t have an explicit native component. Also, in being explicit about versions of the gems that an application is using, we do not run the risk of chasing a moving target. Again, in a shared environment, if someone updates a gem on the system, it’s probably not an issue until the one time it is and you’re being called at 3 AM that the application is down because of a gem incompatibility change.
That’s it. Any other packaging dos and don’t?
This post is totally SFW. That’s all your going to get in this teaser.
I just inherited an application and was adding some tests and noticed that one of the tests was randomly failing. As I dug in more, this particular controller test, in executing the controller’s index method, was actually calling out on the intertubes to request some data. For an integration test that’s probably OK, but my view is that unit and functional tests should be self-contained.
“FakeWeb is a helper for faking web requests in Ruby. It works at a global level, without modifying code or writing extensive stubs.”
3 lines of code later and I have a self-contained functional test that works with real data.
def setup FakeWeb.allow_net_connect = false end def teardown FakeWeb.allow_net_connect = true end test "should get index" do FakeWeb.register_uri(:get, 'http://my.real.url.on.the.internets.com', :body => File.join(File.dirname(__FILE__), '../fakeweb', 'some-file.xml'), :content_type => "text/xml") get :index assert_response :success ...
All I needed to do was simply register the URI that was being referenced in my test, provide a valid (or invalid depending on the test) response, and my test will never try to access the real internet.
Speed is the name of the game. Or is it?
I was adding another project to our Continuous Integration (CI) server that runs the test suite for a project after code is checked in and started noticing the time it takes to build certain projects. A few interesting statistics for you to noodle on.
Average build time: ~32 seconds
Max build time: 1 hour and 48 minutes
There are approximately 18 projects that get built through CI and many other projects that have not been setup for CI.
What does this tell us? Testing is not an impediment to development because it takes too long to run tests. As most test suites run in less than a minute, not running the test suite is not an option for our developers. And if you don’t, CI sends an e-mail to the team letting everyone know you broke the build. And since our post-commit hooks for SVN and git notify our Campfire room each time code is committed, chances are you’re going to get an earful in Campfire telling you to fix the build because your last commit broke the build.
I’m glad nearly all of our projects have test suites that run quickly, but I also have a practical view on test suites. Above all, if nothing else, I want a test suite to exist. I want it to be there so that when I’m adding code to the repository, the test suite is looking at me asking for more.
About that project that takes 1 hour and 48 minutes to build? Right. Guitar Hero. It’s a BIG project. It’s over 3 years old. There is a LOT of code. We test A LOT of systems, e.g. accounts, clans, tournaments, leaderboards, game configurations, game integration processing, etc. I’m OK with it taking that long to run the test suite. We have development practices that we follow for running sub-sections of the test suite when we touch the application to test our new code.