A body of techniques for investigating phenomena and acquiring new knowledge, as well as for correcting and integrating previous knowledge. It is based on gathering observable, empirical, measurable evidence.
That’s how the venerable Wikipedia defines the scientific method. In one of my former careers, systematic observation (through tests and repeated replication of results) was inherent in our every activity. Yet when I took to learning programming, I somehow forgot that the these methods aren’t just useful for gathering knowledge about the natural world, but about everything—including the digital world.
This realization came to me as a little flash of light while reading my brilliant former instructor Mike Clark’s post titled ‘Are you there world?,’ which suggests testing as a first step in learning Ruby. For better or worse, this is actually the exact inverse of the way most of us learn languages and frameworks. Testing is usually one of those things that we intend to get around to someday but never quite take the time to learn properly—something to do at the nebulous “end” of your big project as a way of making sure things aren’t breaking in production. I had fallen into this trap as well. I learned Ruby mostly by randomly stumbling around, trying new things in attempts to build little sandbox projects. There was nothing systematic about this, nor was there any attention to replication of my results. (Most of it involved lots of browser refreshing.) Somehow, I had forgotten the scientific method as the path to knowledge.
Mike, however, advocates what could certainly be called a scientific approach to learning Ruby, based on what are effectively systematic, repeatable, language experiments. He challenges us to put the computer in charge of remembering all the experimentation we do when we’re first learning a language, by writing tests for all the new functionality in the language that we learn as we’re learning it.
Tests, after all, are statements of an expectation followed by a verification that the actual results of an operation evaluate to that expectation. When we’re first learning, we poke at Ruby with a stick (irb) and see how it reacts. Mike is merely suggesting making these observations systematic and repeatable—recording how the subject reacts for posterity so you’ll be able constantly to review and verify what you’re learning about it. Think of yourself as a scientific pioneer who has discovered a completely new and sophisticated organism (in our case, Ruby). Earn that lab-coat of yours: keep good records of your findings!
I’m going to stretch the concept of “replication” from the scientific method a bit here, but I think it also applies as a metaphor at least. The idea is that when you have language tests in place to record what you’ve learned, you always know when your knowledge becomes invalid. When your expectations fail, you have something new to learn. For example, things are in constant flux in the Rails core code base. In 2.0, old ways of doing things are going to start breaking. If you’ve kept a record from the beginning of everything you know about Rails (in the form of an executable test), all you have to do is update your version of Rails, run the test again, and you’ll know everything that you have to learn over again simply by seeing what fails.
I just have one objection to Mike’s article. His article is all about encouraging people to use Ruby’s built-in Test::Unit framework. As it happens, Test::Unit is the main reason I went so long without ever learning testing in the first place. It’s syntax is weird, ugly, and requires significant mental backflips in order to understand what you’re trying to express. This isn’t what we spoiled young Rubyists are accustomed to. Let’s take a look at a gently modified example from Mike’s article
1 2 3 4 |
def test_length string = 'abc' assert_equal(3, string.length) end |
For those of us used to expressions like 2.days.from_now, this reads as utter gibberish: “test length assert equal 3 string equaling ‘abc’ length” That doesn’t make sense in any dialect of Engrish, Franglais, or Spanglish I’m acquainted with. But there is no need to succumb to this aphasia virus; there’s hope.
That hope comes to us in the form of RSpec, the new hotness in the Ruby and Rails testing world. Now, I should preface my discussion of RSpec by warning that most of the people who like to write about RSpec are given to a certain form of intellectual onanism, which forces them to insist on all manner of esoteric precepts and justifications for their framework. Watch out in particular for citings of the Sapir-Whorf Hypothesis, which doesn’t relate to the House of Mogh but rather a unjustifiably baroque way of expressing the simple idea that the the sort of language we use affects the way we think. Put even more simply, it’s better when we express ideas in a way that makes sense.
And that’s really what RSpec is all about: expressing tests in a way that makes sense. Let’s rewrite our Test::Unit example in RSpec terms:
1 2 3 4 5 6 7 8 |
context "A String" do specify "Should return the number of characters it contains when sent the message 'length'" do string = "abc" string.length.should_equal 3 end end |
How does that read in English? Well, we could run the file from the terminal with spec ruby_spec.rb -cfs and we’d get a nicely formatted answer in the output.
A String
-Should return the number of characters it contains when sent the message 'length'
(In fact, if you do sudo gem install redgreen, you’ll even get that second line in green if its spec passes, or red if it fails.)
Or, alternatively, just read the code itself, “A string should return the number of characters it contains when sent the message ‘length’: string equaling ‘abc’, length should equal 3.” How does that compare to “test length assert equal 3 string equaling ‘abc’ length” Yes, it’s more verbose, but that’s because it actually expresses an intelligible idea.
Incidentally, this syntax gives us the added advantage of rendering us so pliant and at ease with tests (because they’re not expressed in such a weird backwards-thinking way) that it feels much easier actually to write them before we write the code we want to test. This test-first approach is something many of us believe to be a vastly superior way of doing software design (aka Behavior Driven Development, or BDD, aka Test Driven Development, or TDD). This delay-of-gratification approach to coding normally requires significant restraint and patience, but with RSpec, it just feels right. Expressing tests/specs/expectations (or whatever you want to call them) in an intuitive way makes it easier to think about how you want your application to behave, allows you to specify that behavior, and then test that it meets the specification in what feels like natural language.
As an aside, there are those who, in order to protect the new hotnessness of their respective fiefdoms, will flip their wigs at the mere suggestion that TDD and BDD are similar/interchangeable concepts. You’ll have to forgive their casuistry. They mean well.
Now that I have this whole new, completely intuitive way of expressing and testing expectations in Ruby, I find myself actually writing tests all the time, for everything. Both as a learning tool and as a design tool. Now, I’m working with my pals over at The Edge Case to learn more about its advanced features, like “mocking,” “stubbing” and other tricks to help me avoid those nasty Rails fixtures. So look for more reports from the frontiers of RSpec science here in the future.
Mike Clark is right: the scientific approach is the best way to gather knowledge (and by extension to learn Ruby). There has just been a little paradigm shift in our scientific approach. Give RSpec a try, and see if it doesn’t make that lab-coat fit a little more comfortably.




February 23rd, 2007 at 12:25 PM Oh c'mon, Test::Unit is not *that* bad. RSpec only addresses the language of assertions (from what I've seen) which really is not a big issue to me. I don't see Test::Unit as forcing you to do "mental backflips" in the least. As far as the English, use comments or say "def test_string_length_returns_the_number_of_characters_it_contains"... verbosity is not magic. Other issues, like mocking and stubbing, however, are useful and critical to doing testing easily and quickly. I have begun to use Mocha but it can still be very difficult to limit a test's scope as your code increases in complexity.
February 24th, 2007 at 04:28 AM My point isn't that Test::Unit is unusably awful or anything, merely that RSpec is a sufficient incremental improvement to nudge my lazy ass towards actually writing a test here or there. Comments to make our code read like English? Like I said, we should set our expectations high. This is Ruby. :P Mocking and stubbing are quite cool, and I've only recently started playing with them. But they are really only ancillary parts of RSpec--not central to all the hubbub about the framework, by most people's writing anyway. Luke Redpath's article, which seems to be the most linked-to tutorial on RSpec makes no mention of mocking or stubbing.. The next topic I'd like to write about is how you can use mocks/stubs, along with a few other RSpec tricks to almost entirely avoid having to write fixtures, which are an annoying and brittle mess to deal with if you have lots of models with multiple cascading associations.
February 26th, 2007 at 02:53 PM Incidentaly, here is an interesting new development in RSpec core. http://blog.davidchelimsky.net/articles/2007/02/21/generated-spec-names-keep-specs-dryer RSpec will now write the name of the method for you based on your should statements. It's much more DRY this way. You can always override with your own method name if you don't like what ends up in your spec doc based on RSpec's guesswork.
March 4th, 2007 at 02:25 AM IMO it's a brilliant way to learn Ruby/Rails from any of the available books. Just grab the code which accompanies the book, make a new rails project, put the code inside. And start to write specs/tests as you read the book. Discover the code and eventually language/framework itself as rocket surgeries do! :) P.S. Great article, thx!
May 7th, 2008 at 01:40 AM Huge Round Tit
May 10th, 2008 at 09:39 AM Preteen!! Preteen | http://google.com/group/young-videos/web/