Software Development
Specifying raising of errors in RSpec
by Doug on Mar.24, 2008, under Behaviour Driven Development, Ruby
RSpec allows a developer to specify that an error is raised within a block with the raise_error method. It’s a nice expressive way of saying that your code should fail when it needs to.
But my tiny brain has often been confused with using it at times, more so when the error class requires parameters for instantiation and when used in conjunction with the and_raise method on a stub or a mock
Consider the snippet below where my Widget depends on Thingamajig to do its funky thing in order to run. But Thingamajig is rigged to explode in a mass of funkyness and make Widget all useless.
[code]
describe Widget do
it "should re-raise errors as a Widget::UnrecoverableError" do
# expectations
thingamajig = stub(Thingamajig)
thingamajig.should_receive(:do_funky_thing).and_raise(Thingamajig::FunkyExplosion.new('The funky thang exploded yo'))
# our SUT
widget = Widget.new(thingamajig)
#verification
lambda { widget.run }.should raise_error(Widget::UnrecoverableError, 'The funky thang exploded yo')
end
end
[/code]
Do you notice the inconsistency between the way errors are declared in the expectation and the actual verification?
[code]
# expectations
thingamajig.should_receive(:do_funky_thing).and_raise(Thingamajig::FunkyThingExplosion.new('The funky thang exploded yo'))
#verification
lambda { widget.run }.should raise_error(Widget::UnrecoverableError, 'The funky thang exploded yo')
[/code]
The expectation on the stub, ‘thingmajig’ needs the exception instantiated first while verification requires the class name and parameters used to instantiate the error instance.
And no, doing it like this doesn’t work as expected:
[code]
lambda { widget.run }.should raise_error(Widget::UnrecoverableError.new('The funky thang exploded yo'))
[/code]
It’s an unfortunate impedance mismatch that might be caused due to the way Ruby handles the raising of errors.
Using cruisecontrol.rb with repositories without anonymous access
by Doug on Feb.02, 2008, under Agile, Ruby
I decided to use cruisecontrol.rb for continuous integration of an application that I’m working on at Bezurk. So I downloaded the 1.2.1 release from RubyForge and proceeded to add the project repository to the local installation of cruisecontrol.rb.
douglas@macbookpro:~$ ./cruise add MyProject --url http://path.to/repository --username 'douglas' --password 'guessable' douglas@macbookpro:~$ ./cruise build MyProject Builder for project 'MyProject' started Logging to: /Users/douglas/Development/Ruby/cruisecontrolrb-1.2.1/log/MyProject_builder.log Build loop failed BuilderError: svn: PROPFIND request failed on '/svn/my_project/trunk' ./script/../config/../app/models/subversion.rb:98:in `execute_in_local_copy' ./script/../config/../lib/command_line.rb:86:in `call' ./script/../config/../lib/command_line.rb:86:in `e' ./script/../config/../lib/command_line.rb:84:in `popen' ./script/../config/../lib/command_line.rb:84:in `e' ./script/../config/../lib/command_line.rb:71:in `execute' ./script/../config/../lib/command_line.rb:70:in `chdir' ./script/../config/../lib/command_line.rb:70:in `execute' ./script/../config/../app/models/subversion.rb:89:in `execute_in_local_copy' ./script/../config/../app/models/subversion.rb:85:in `chdir' ./script/../config/../app/models/subversion.rb:85:in `execute_in_local_copy' ./script/../config/../app/models/subversion.rb:44:in `latest_revision' ./script/../config/../app/models/project.rb:228:in `new_revisions' ./script/../config/../app/models/change_in_source_control_trigger.rb:8:in `revisions_to_build' ./script/../config/../vendor/rails/actionpack/lib/../../activesupport/lib/active_support/core_ext/symbol.rb:10:in `__send__' ./script/../config/../vendor/rails/actionpack/lib/../../activesupport/lib/active_support/core_ext/symbol.rb:10:in `to_proc' ./script/../config/../app/models/project.rb:223:in `collect' ./script/../config/../app/models/project.rb:223:in `revisions_to_build' ./script/../config/../app/models/project.rb:202:in `build_if_necessary' ./script/../config/../app/models/polling_scheduler.rb:13:in `run' ./script/builder:79 ./script/builder:78:in `catch' ./script/builder:78 ./cruise:14:in `load' ./cruise:14:in `builder' ./cruise:68:in `send' ./cruise:68 /opt/local/lib/ruby/1.8/fileutils.rb:121:in `chdir' /opt/local/lib/ruby/1.8/fileutils.rb:121:in `cd' ./cruise:67
Hmm, what’s with the svn: PROPFIND error? Looking at the stracktrace doesn’t tell me alot about what’s going wrong here, let’s try logging errors to the console.
--SNIP-- douglas$ svn --non-interactive info --xml douglas$ svn --non-interactive log --revision HEAD:20 --verbose --xml svn: PROPFIND request failed on '/repository/trunk' svn: PROPFIND of '/repository/trunk': authorization failed (http://svnhost.com) --SNIP--
It happens that my repository does not have anonymous access and requires a subversion user account to do anything useful. So it should be obvious that cruisecontrol.rb is trying to get log info from the repository but subversion is quitting with authentication errors because no user credentials are being supplied.
I need to have cruisecontrol.rb make use of the –username and –password options when making queries to the repository when I give it the credentials for access.
My first stop is the app/models/subversion.rb. Only the checkout method uses the username and password instance variables. Subversion should only include the –username and –password options when executing svn commands when both the username and password instance variables are present.
[ruby]
test/unit/subversion_test.rb
def test_svn_command_uses_user_password_when_provided
svn = Subversion.new(:username => 'jer', :password => "crap")
svn.expects(:info).with(dummy_project).returns(Subversion::Info.new(10, 10))
svn.expects(:execute).with(["svn", "--non-interactive", "log", "--revision", "HEAD:10", "--verbose", "--xml",
"--username", "jer", "--password", "crap"],
{:stderr => './svn.err'}).yields(StringIO.new(LOG_ENTRY))
svn.latest_revision(dummy_project)
end
app/models/subversion.rb
def checkout(target_directory, revision = nil, stdout = $stdout)
@url or raise 'URL not specified'
options = [@url, target_directory]
options < < "--revision" << revision_number(revision) if revision
# need to read from command output, because otherwise tests break
execute(svn('co', options)) do |io|
begin
while line = io.gets
stdout.puts line
end
rescue EOFError
end
end
end
def svn(operation, *options)
command = ["svn"]
command << "--non-interactive" unless @interactive
command << operation
command += options.compact.flatten
command += ['--username', @username, '--password', @password] if @username and @password
command
end
[/ruby]
The username and password would then be injected into the project's Subversion instance in the cruise_config.rb file for each project.
[ruby]
Project.configure do |project|
project.source_control.username = 'douglas'
project.source_control.password = 'guessable'
end
[/ruby]
I’ve submitted a ticket along with a patch for this on cruisecontrol.rb’s tracker. Keep a lookout for it if you happen to encounter the same problem.
Writing tests first is…?
by Doug on Dec.27, 2007, under Software Development
What would you do if your co-developers say “Writing tests first is too hard/slows me down/waste of time”?
Behaviour Driven Development != Testing
by Doug on Dec.23, 2007, under Behaviour Driven Development
Testing: Executing a program with the specific intent of uncovering errors.
Software Engineering: A Practitioner’s Approach – Roger S Pressman
The definition of testing by Pressman states that the purpose of performing software testing is to detect errors in a program. This encompasses a wide range of techniques such as black box/white box testing, basis path testing, fault based testing and at a more thorough level, control structure testing.
So where do the specifications in Behaviour Driven Development (BDD) fit into the picture? In this respect, BDD isn’t about testing at all. We write specifications to say that the software exhibits a specific behaviour when its in a certain state. The specifications serve to reinforce the notion that the program is working as expected under known conditions.
Having 100% coverage for code certainly does not mean that a program is free of errors, there are still edge cases that may be too difficult or complex to replicate with an automated test suite. Traditional QA testing is still very much relevant to software projects with BDD employed religiously throughout development.
This has been discussed at length before but it bears repeating: BDD is a design technique that gives you executable documentation of what functions the software is expected to provide.
Treat code like prose
by Doug on Oct.11, 2007, under Software Development
Love this explanation of refactoring by Michael O McCracken.
From J.B Rainsberger.
Listening to your tests
by Doug on Aug.26, 2007, under Behaviour Driven Development
One of the challenges I’ve been trying to overcome in practicing Test First Development(TFD) has been making sense of the feedback that comes from TFD. It was not obvious to me till recently, after I’ve read an excellent article (IEEE Explorer account required) by Bas Vodde and Lasse Koskela in IEEE Software. Bas and Lasse recount their experiences in conducting TFD workshops in Nokia and in particular the insights gleaned from a TFD coding exercise.
One key point made by the authors was that although the participants in the coding exercise followed the test-code-refactor cycle, their code became progressively complex and littered with nested branching constructs. It made keeping track of the software’s behaviour difficult. Bas and Lasse observed that once the the initial design approach was chosen, none of the participants thought about whether the design was still suitable for the current requirements.
Essentially, the test-code-refactor cycle was taking longer to complete and the code was turning out to be an unmaintainable mess. This feedback was lost on the participants and while some decided to hide the code’s complexity behind refactorings that make the code read better, others simply added more tests and attempted to make them pass.
It should be obvious that emergent design will only occur when there is constant reflective thinking about the state of the code. This takes a bit of skill and confidence on the part of the developer. Simply going through the motions of test-code-refactor to the simplest design without this reflective thinking will lower the effectiveness of TFD as a design technique.
Using Factories for Rails Fixtures and Test Doubles
by Doug on Aug.11, 2007, under Behaviour Driven Development, Rails, Ruby
Chris Wanstrath has written about making Rails fixtures less painful than they need to be with the FixtureScenarios plugin. Personally, I prefer the Factory approach, nicely explained by Daniel Manges.
I’ve been using factory methods to create in-database ActiveRecord objects for a project that I’ve been working on in Bezurk. Reading Daniel’s article gave me a few ideas on improving the way I create fixtures and mocks. Since I’ve been using RSpec extensively in this project, I’ll present the examples in RSpec.
As the models evolve with the design and its behaviour change accordingly, there is a need to go through all the specifications that create this model and make sure that its created in a valid state. This is more pronounced with the use of test doubles, the test doubles also need to have its method stubs changed to reflect the latest state of the model that its is representing. I happen to make much use of test doubles for test isolation, so trying to manage all these objects became an exercise in patience. As it was getting painful, It’s time to change the way I create these models and test doubles.
As always, a layer of indirection will always go some way to solving a software problem. We introduce a Factory that encapsulates the creation of ActiveRecord objects by providing creation methods.
[ruby]
module FixtureFactory
def create_user(attributes = {})
User.create!(ModelAttributes.user(attributes))
end
end
[/ruby]
We’ll have a Factory for test doubles too.
[ruby]
module MockFactory
def mock_user(method_stubs = {})
mock_model(User, ModelAttributes.user(method_stubs))
end
end
[/ruby]
And the attributes for this model will be declared in a module that’s used by both Factories
[ruby]
module ModelAttributes
def self.user(attributes)
attributes.reverse_merge({:name => ‘doug’})
end
end
[/ruby]
The Factory modules are then included in Spec::Runnner
[ruby]
Spec::Runner.configure do |config|
include FixtureFactory
include MockFactory
end
[/ruby]
The objects can now be created using the factory methods available to all specifications.
[ruby]
doug = create_user
doppelganger = mock_user
[/ruby]
Update
Added links to Chris Wanstrath and Daniel Manges’ articles on managing Rails fixtures.
Behaviour Driven Development with RSpec slides
by Doug on Jul.26, 2007, under Behaviour Driven Development, Ruby
Here’s my slides for the talk on Behaviour Driven Development and RSpec at the July 2007 singapore.rb meeting. It might not make much sense if you weren’t at the meeting.
RSpec your functional tests
by Doug on May.10, 2007, under Behaviour Driven Development, Ruby
The release of RSpec 0.9.4 can be considered a landmark release of sorts. With the inclusion of Spec::Ui 0.2.0, RSpec can now perform functional testing of web applications within a browser. Out of the box support is limited to Watir/Internet Explorer for now as the Selenium RC custom matchers are not implemented yet. includes WATIR/Windows and Watirsafari/OSX. The custom matchers for Selenium RC are not implemented yet but its certainly possible to use Selenium with Spec::Ui, it just won’t read as nicely compared to WATIR. Spec::Ui also comes with a custom result formatter that packages a screenshot of the browser on spec failure, along with its HTML source in the report.
To use Watirsafari in your functional specs, you’ll need to install the gem for it: gem install watirsafari
spec_helper.rb should be updated to include the snippet below (code lifted from the original release announcement):
[ruby]
Spec::Runner.configure do |config|
config.include Spec::Matchers::Watir
end
[/ruby]
The specification will need before and after blocks like this:
[ruby]
before(:all) do
@browser = Watir::Browser.new
end
after(:each) do
# This is needed to make screenshots work
Spec::Ui::ScreenshotFormatter.browser = @browser
end
after(:all) do
@browser.kill! rescue nil
end
[/ruby]
There are a lot more useful information available in the examples supplied with Spec::Ui. Take a look at the samples to get up and running.
Update
My thanks to Aslak Hellesoy for correcting me on support for WATIR and Selenium.
Using Textmate for Actionscript
by Doug on Feb.10, 2007, under Software Development
I’ve never liked the default code editor that comes with the Flash IDE, so when the I discovered that I would need to develop actionscript code for a project, my first thought was to use Textmate instead. Incidentally, some people are already doing this.
I’ve used MTASC to compile Actionscript files before, the most important difference that surfaces when comparing it to Macromedia’s Actionscript compiler (MMC), which is the one that’s packaged in every installation of Flash, is that MTASC is much stricter on enforcing proper syntax. This is intended to reduce occurences of hard to find bugs that arise from improper scripting. The other feature that MTASC touts is its faster compile speed, however I’ve not experienced a visible variance between it and MMC. I would think that this speed improvement would be more apparent when compiling large projects consisting of more than 50 Actionscript files.
I’m using dirtystylus’ Textmate command for checking the syntax of my files, it took me a while to get it working as I organised the dependencies differently. Following my conventions in using MacPorts which is installed in /opt/local/, MTASC and XTrace were placed in /opt/local/managed/. The executable for MTASC was stored in /opt/local/managed/bin/ while XTrace.app was copied to the Applications folder. All supporting scripts were moved to /opt/local/managed/lib/.
This is the customised command that I’m using after trying for about 2 hours.
[code]
FLASHPLAYER=SAFlashPlayer
MTASC=/opt/local/managed/bin/mtasc
CLASSPATHS="-cp /opt/local/managed/lib/mtasc/std/ -cp /opt/local/managed/lib/mtasc/std8/ -cp /Users/douglas/Library/Application\ Support/Macromedia/Flash\ 8/en/Configuration/Classes/"
SOURCE="$TM_FILEPATH"
OUTPUT=test.swf
VERSION=8
TRACE="-trace com.mab.util.debug.trace"
TMPFILE=/tmp/as-compile.err
compileResult=`$MTASC -main "$SOURCE" -wimp -version $VERSION -strict $CLASSPATHS $TRACE 2>&1`
echo "
"
if test -n "$compileResult"
then
errorLine=`echo $compileResult | sed 's/.*:\([0-9]*\):.*/\1/'`
echo "$compileResult";
else
echo "
fi
[/code]