TDD and random numbers in ruby
February 1, 2008
I’m about to TDD a Ruby class whose behaviour will involve the use of random numbers. I expect the algorithms within the class to evolve as I implement new stories, so I don’t want to design and build a testing mechanism that will be brittle when those changes occur. But before I can write the next example, I need to figure out how to control the random numbers needed by the code under test. Off the top of my head I can think of four options:
- One way would be to set a fixed seed just before each test and then simply let the random algorithm do its thing. But for each new behaviour I would need to guess the appropriate seed, which is likely to be time-consuming. Furthermore, the relationship between each magic seed and the specific behaviour tested is likely to be obscure, possibly requiring comments to document it for the future reader. And finally, if the algorithm later evolves in such a way as to consume random numbers in a different order or quantity, the seed may turn out to be inappropriate, leading to broken tests or, worse, tests that pass but which no longer test the planned behaviour.
- Alternatively I could redefine
Kernel::rand— but that could potentially interfere with stuff I don’t know about elsewhere in the object space. - Within the class under test I could self-encapsulate the call to
Kernel::rand, and then override the encapsulating method in a subclass for the tests. But then I’m not testing the class itself. - Finally, I could parameterize the class, passing to it an object that generates random numbers for it. This option appears to give me complete control, without being too brittle or trampling on anyone else in the object space.
So I’ll go with option 4. Right now, though, I’m not sure what interface the randomizer object should provide to the calling class. Looking ahead, I expect I’ll most likely want to select a random item from an array, which means selecting a random integer in the range 0...(array.length). And for this next example all I’ll need is a couple of different randomizers that return 0 or 1 on every request, so I’ll simply pass in a proc:
obj.randomize_using { |ceil| 0 }
And if ever I need to provide a specific sequence of varying random numbers, I can do it like this:
rands = [1, 0, 2]
obj.randomize_using { |ceil| rands.shift || 0 }
Later that same day…
The class I’m developing has evolved quite a lot and split into three. And suddenly, with the most recent change, three of the tests have begun failing. A little investigation reveals that the code is now consuming a random number when it didn’t need to in the past, and so some of my randomizer procs now provide inappropriate values. It turns out that two of the failing examples actually boil down to being a single test of a piece that has now been refactored out into another class; by refactoring the tests to match I can remove the dependency on random numbers altogether. And the last broken test is fixed by providing a randomizer that respects the ceiling passed to it (not an unreasonable request):
obj.randomize_using { |ceil| [2, ceil-1].min }
This works, and I get no more surprises during the session.
a fluent interface for numeric literals
December 19, 2007
In The Cardinality of a Fluent Interface Michael Feathers posed a code kata in which one has to create a DSL in which constructs such as the following all provide the expected numeric value:
one two twelve twelve.hundred one.hundred.thousand.and.fifteen twelve.hundred.and.one five.hundred.and.fifty.seven
My first pass at a solution in Ruby is listed below (and a copy with unit tests can be downloaded from my website). There’s still some duplication in there, and hopefully I’ll get around to removing it soon.
In his post, Mike asks whether the DSL’s “cardinality” — the number of classes required to implement the DSL — says anything about the DSL’s grammar. Well, my solution has 7 classes (although PartialValue exists only to remove a little duplication). I have no idea what that might mean in the DSL world.
Read the rest of this entry »
gravity and metaphors at the MDDA
October 29, 2007
Dave Verwer at NWRUG has been crazykind enough to invite me to re-run a version of my hexagonal architecture workshop this month at the MDDA. This is the session that, two years ago, had seventy of the world’s leading agilists standing on one leg. If you’re in Manchester on the evening of November 20th, why not hop along?
a ruby sparkline showing variation
July 25, 2007
Back in May’s Carnival of the Agilists I referenced a post by Clarke Ching in which he suggests we can learn a lot about variation in a complex process by simply flipping coins. When I tried the simulation a few times with Excel I found, as expected, that heads and tails don’t always occur in equal measure. But that was a pain to do, so I’ve made it easier. Over on my website I’ve posted a page that runs the simulation and shows the results as a sparkline graph. Every time you refresh that page it re-runs the simulation, with different random tosses of the imaginary coin.
(Disclaimer: The image is an SVG. It works fine in Firefox, but if you use IE you may need to install some kind of viewer.)
The sparkline itself is an SVG image generated by a Ruby script. If there’s enough interest I’ll turn it into a proper Ruby library and perhaps even gemify it. What features would you like to see if / when I do that?
setup and teardown for a ruby TestSuite
June 9, 2007
My Watir tests for a particular application are grouped into three subclasses of Test::Unit::TestCase. To run them all I have a top-level test suite that looks like this:
require 'test/unit' server.start require 'test/first_tester' require 'test/second_tester' require 'test/third_tester' server.stop
But this doesn’t work as intended, because the server.stop line at the end of the script is executed before the test suite is constructed and executed, which is obviously not what I want. The problem is in test/unit: the require causes this script to collect every test method in the ObjectSpace and then invoke the runner on the resulting suite.
What I would like is to have setup and teardown methods on TestSuite; but they aren’t there. I feel sure someone somewhere must have done this already (the closest I could find was this old post on ruby-talk), but I couldn’t find one quickly so I wrote my own:
require 'test/unit/testsuite'
require 'test/unit/ui/console/testrunner'
require 'test/first_tester'
require 'test/second_tester'
require 'test/third_tester'
class TS_MyTestSuite < Test::Unit::TestSuite
def self.suite
result = self.new(self.class.name)
result << FirstTester.suite
result << SecondTester.suite
result << ThirdTester.suite
return result
end
def setup
server.start
end
def teardown
server.stop
end
def run(*args)
setup
super
teardown
end
end
Test::Unit::UI::Console::TestRunner.run(TS_MyTestSuite)
Please let me know if this solution - or anything equivalent - is already published elsewhere, because I hate re-inventing wheels…
ruby is fast maturing
May 23, 2007
Here’s a quote from RailsConf2007 that I find astonishing:
“There seems an excellent chance that Ruby and Rails could become a significant platform for IT develop over the next few years. We’re already seeing signs of this at ThoughtWorks - 40% of our new business this year in the US is Ruby work.” — Martin Fowler
Forty percent?! That’s amazing.
I do now find myself using Ruby freely as the main scripting and general project automation tool, but always with a hint of nervousness. Because it isn’t yet sufficiently mainstream that I can expect my scripts to run everywhere. There’s still some way to go before I can expect to be able to ship Ruby-enabled stuff to end-user sites, for example. And even within development shops I find the spread of all the bits n bobs (eg. the copious numbers of gems required in order to do anything unusual) is still very sparse. Maybe JRuby will help.
The rest of Martin’s conference report is well worth a read, as are the many others appearing in our aggregators this week. I’m not sure Ruby will help us end world poverty, but that figure of 40% is possibly a (startling) sign of things to come.
revised maze refactoring challenge
April 22, 2007
Eighteen months ago I posted a Ruby version of Bill Wake’s Amazing maze refactoring challenge. I looked at it again this week and discovered that it is rubbish: the transliteration from Java is not very literal, and one of the test examples is broken. So I’ve re-created it and uploaded a revised version. My apologies if you tried working from the original version.
win32-process quirks
March 5, 2007
I had occasion to install the win32-process gem today, because my Watir tests needed to start a background agent. On my laptop the cookbook example in the gem’s documentation doesn’t work; I found:
- The package to require is ‘win32/process’;
- the block version of
fork()causes the child process to execute the block and then continue with the parent code; - the module names in the examples are incorrect (and not needed); and
- the return value from
Process.create()is not a process ID, but a structure that contains a process ID.
Nothing major, but it wasted twenty minutes. Anyway, for completeness here are a couple of corrected example scripts showing the behaviour of win32-process. First, mimicking the old Unix API:
require "win32/process"
pid = Process.fork
if pid.nil?
3.times {
puts "In the child"
sleep 1
}
exit
end
2.times {
puts "In the parent"
sleep 1
}
Process.wait
As expected, this produces
In the parent In the child In the parent In the child In the child
Now try passing a block to the child:
require "win32/process"
Process.fork do
3.times {
puts "In the child"
sleep 1
}
exit
end
2.times {
puts "In the parent"
sleep 1
}
Process.wait
On my PC this produces:
In the parent In the child In the parent In the child In the child In the parent In the parent
Those extra prints from the parent show that the child process continued after completing the block!
setting up eclipse for rails
July 24, 2006
I’m starting out on two new Rails projects this week, so I’ve been investing some time in getting Eclipse configured nicely. There are dozens of helpful websites containing helpful advice, and after much trial and error I found a couple of these that worked really well for me.
For setting up RDT (the Ruby Development Tools) I found Brian Hogan’s Setting up a Rails Development Environment on Windows Using Eclipse to be the most complete and accurate resource. However, it doesn’t cover RadRails. For that, I recommend Victor Kane’s RadRails Tutorials. Both of these sites provided complete and correct advice that worked straight out of the box.
But only after I had spent much of last week struggling with the Cygwin version of ruby. I’ve been an old Unix hand since before I could walk (I wrote some small parts of the System V kernel, would you believe). So when I work on a Windows box, one of my first actions has always been to install Cygwin and make it seem a little more like “home”. But not any more. It’s just too hard to get Cygwin, Ruby, gems, Eclipse, Rails, Apache, MySQL and Subversion all working together. Either the DLL versions are in conflict with each other, or something coredumps regularly, or something needs to be compiled with one in order to work with the other. At every turn I came up against stuff that was known not to work. So in the end I have reluctantly decided that I must be congruent, and not try to pretend that Windows is Unix. It was a painful decision, but so far it has paid off.
Now, to get on with some development…
can you recommend a hosting service?
June 30, 2006
I have a little toy in development, and I’m looking for a different hosting service, because my current lot don’t seem in the least inclined to support Ruby, let alone Rails. So can you recommend a great host? Here’s what I’m looking for:
- full support for Ruby and Rails
- install my own gems as required
- Perl
- CGI
- reasonable control over .htaccess files
- huge MySQL databases
- shell access
- ftp access
- domain hosting, preferably with control over DNS entries
- friendly, honest and transparent support
- business accounts permitted
- upto 1GB disk space
- crontabs
- must be secure, and have high availability
- preferably UK based
Oh, and it must be cheap! Thanks for any recommendations.






