Recently I was doing a simple kata – the Roman Numeral kata – to practice my Ruby and RSpec skills. (The Roman Numeral kata is to build an algorithm test-first that converts a number into its Roman numeral equivalent. For example, 1 to i, 7 to vii, 10 to x, etc. The problem is intended to be simple so that you can focus on the process.) I had the following RSpec code:
require 'roman_numeral'
describe RomanNumeral do
# specify is just an alias for it. specify reads better in this case
specify '1 should be i' do
numeral = RomanNumeral.new(1)
numeral.to_s.should == 'i'
end
end
I go through the Red/Green/Refactor cycle and get this first spec working. (I won’t show the production code as it’s not that interesting.) I move onto the second spec.
require 'roman_numeral'
describe RomanNumeral do
specify '1 should be i' do
numeral = RomanNumeral.new(1)
numeral.to_s.should == 'i'
end
specify '2 should be ii' do
numeral = RomanNumeral.new(2)
numeral.to_s.should == 'ii'
end
end
Red/Green/Refactor and all is good. But I’m seeing some repetition in my specs and it’s only going to get worse. The specs are almost identical except for the data. So I start hunting through the RSpec docs for something akin to NUnit’s [TestCase] or xUnit’s [Theory]. I find nothing. Maybe, like MSpec, RSpec wasn’t designed to do this sort of data-driven testing.
Undaunted I turn to Google and stumble upon this StackOverflow question asking exactly the same question that I had. Matt Di Pasquale, the OP (original poster), found his own answer and that answer was fascinating! No, RSpec doesn’t have a syntax for test cases because it doesn’t need one. Use the Ruby, Luke!
require 'roman_numeral'
describe RomanNumeral do
cases = {
1 => 'i',
2 => 'ii'
}
cases.each do |k, v|
specify "#{k} should print as #{v}" do
numeral = RomanNumeral.new(k)
numeral.to_s.should == v
end
end
end
For those not as well-versed in Ruby, “cases” is simply a variable that contains a hash. I then iterate over the key/value pairs and define my specifications. It’s that simple. I didn’t need any special attributes or framework support from RSpec. I didn’t need to learn the inner workings of RSpec to write my own custom extension or attributes. I simply wrote some Ruby code. Let that sink in for a second. I simply wrote some Ruby code.
Now think about that a bit more. I’m writing regular Ruby code to implement the notion of test cases. Rather than defining test cases, I could have used very similar code to define a benchmark that verifies an algorithm scales linearly with number of input elements. Or I could have fuzz tested an external API for my application. The possibilities are only limited by my imagination and don’t require me to gain an ever deeper understanding of my testing/specing framework to implement.
For someone who cut his teeth on C, C++, and C# (with some JavaScript thrown in for good measure) and is used to learning (or building) frameworks to solve problems, the notion that you can just write plain old code to solve these types of meta problems is eye-opening. It is one of those ah-ha moments that matures you as a developer. Not every problem needs a framework to solve it. Sometimes it just requires a little code.
As some of you might have noticed, I’ve been talking about Ruby and Ruby on Rails more recently. It’s often good to get outside your comfort zone and see how other (web) developers live. One of the areas where Ruby really excels is the wealth of innovative testing/spec’ing libraries available. When writing tests/specs, we need objects to play with. Ruby has a variety of options in this regard, but I particularly like the factory approach espoused by gems such as
To answer a few burning questions that I’m sure people have… I’ll still be based out of Calgary, Alberta, Canada. I’ll still be speaking at various conferences and user groups – and not just about JetBrains products. (It will be the usual mix of software practices, development techniques, and technologies. So expect to see me at conferences and events both as a speaker and representing JetBrains.) I’ll still be producing Pluralsight videos. (NHibernate Fundamentals will be available soon and I’m already work on Git Fundamentals. JetBrains and Pluralsight will continue to partner on various initiatives.) I’ll still be involved in open-source software. And I’ll still be causing the usual amount of trouble on Twitter and elsewhere. Happy coding!
System.Type and XML are the nuts and gum of the development world. You can stuff a System.Type into an XML file, but it leaves a bad taste in your mouth. Sometimes you don’t have much choice. For example: