Bodaniel Jeanes
I am an Australian software craftsman living and working in Chicago. I strongly advocate open-source software and the community around it. I currently work primarily in Ruby and Rails but have recently been doing more and more Javascript and Clojure. I use Vim.
Having maintainable code is great. Maintainable code allows you to deliver improvements faster, happier, and more reliably.
Furthermore, the measures that developers need to take and the strategies that we have to employ to achieve maintainable code have been understood for years, if not decades. Especially in the realm of object-oriented programming, but certainly not exclusively, most of these principles boil down to reducing coupling and system complexity. A system whose parts are coupled as loosely as possible is a modular system; the parts know little of each other and a lot about themselves and they have thin and specific interfaces between each other.
Test-driven development is one of the many tools at a developer's disposal to achieve code quality. Unfortunately, there is a lot of naïveté around the benefits of TDD. A lot of developers see TDD as primarily a tool for verifying system correctness. While TDD does of course offer this benefit, and arguably better than retroactive automated testing, the real benefit of TDD is that it offers short feedback loops that guide the design/architecture of the system.
Since it is accepted that a loosely-coupled modular system is a simpler system, it stands that tools, such as TDD, which guide a design towards modularity and simplicity are good tools. A module that is tightly coupled to another is not easily tested in isolation. However, if the isolated tests are written first, it is difficult to write a passing implementation for that module that maintains such a low degree of coupling. Therefore, good TDD should guide you towards a simpler design (though it is certainly not the only way).
Unfortunately, factories work against this goal. Factories debilitate TDD's ability to give you feedback into the complexity of your design.
To be clear, I am not talking about the Factory Method Pattern
or the Abstract Factory Pattern — both of which can be described
as ways to decouple a particular implementation
of an object from code for the creation of such an object
(Wikipedia). Instead, I am talking about the "factories" for replacing fixtures in tests — something which has seemingly obsessed the Ruby (Rails, especially) community. The two primary Ruby libraries for factory-based fixture replacement are factory_girl and Machinist.
A tool such as Machinist or factory_girl generates data for the attributes you don't care about, and constructs any necessary associated objects, leaving you to specify only the fields you care about in your test
(from Machinist's own README file). This sounds nice at first, because it makes your tests more readable and relevant. However, behind the scenes, these tools are still creating other objects and entities and introducing them into your test environment. By having data and objects in tests that are irrelevant to the functionality that is being tested (in isolation, remember), a developer creates an environment that permits, if not invites, silent dependencies to creep into an implementation.
Furthermore, and perhaps more significantly, by creating objects (and usually entire hierarchies of objects) with such ease and opacity, you are outright masking the dependencies (*cough* complexity *cough* coupling) between your implementation and those entities. If forced to stub out all those intricacies, the system complexity would be screamingly obvious and a developer would quickly avail herself of a rewrite to reduce complexity or thin out the interface.
Instead of having feedback that guides a developer to simplicity, fixture factories seem to guide developers to complexity by masking dependencies as one-line simplicity. In fact, that one (or five, whatever) line setup is a shotgun blast of environmental dependencies that are hidden from the architect. That complexity will come back for revenge after being ignored for so long.
It seems that factory_girl and Machinist exist to make testing components more convenient. This is, at face value, an admirable and desirable goal. However, in unit tests, the cost is too high for any system of considerable size.
Please, do the right thing and avoid the convenience and "fun" of the factory_girl temptress. You will trend towards a simpler system and as a bonus (in fact, an incredible one) your test suite will likely be exponentially faster which, in turn, will breed simplicity by letting you have more feedback more often.
P.S. It shouldn't go without mention that factories can be absolutely awesome for integration tests. Integration tests aren't used for guiding system design nor testing in isolation so the drawbacks of these tools drop away. However, both factory_girl and Machinist use RSpec as some of their very first usage examples and this troubles me deeply.
P.P.S. A lot of these arguments can be applied to fixtures too. However, they usually don't create hordes of objects invisibly and litter your environment with them. Also, they aren't as slow. But yes, the fewer factories and fixtures in a test, the better.
Update for clarity: Firstly, I am absolutely just talking about unit tests. If you are testing code that integrates with ActiveRecord or number of levels of your stack, then factories and fixtures are certainly defensible (though I still prefer to steer clear). Secondly, I've tried to be careful about where I use the words simple, easy, complex, and difficult. For the definitions that I intend, please watch (at least the first 10 minutes of) Simple Made Easy.
Just a quick post to point out the new design for this blog. It's nothing special — I just needed a change.
Hopefully, the change of aesthetics will motivate me to get back into blogging.
Let me know if there is any weird display issues on your browser. I only tested the latest versions of non-IE browsers, but if there are any stupid obvious fixes to any browser, I'll implement them.
Tell me what you think, keeping in mind I'm a developer not a designer :P.
Although long overdue, I’m here to tell you why I joined Obtiva and why
I think you should too.
About 10 months ago, I found myself looking for a job. I had plenty of
freelance work and plenty of time to find the right fit for myself. With
this in mind, I decided to adopt a new philosophy to apply in situations
like this.
I realised that you don’t always get the full potential out of life
until you face up to what you really want out of it.
The core principle of this outlook was inspired by reading a fantastic
blog post that had one message: If you
can’t say “HELL YEAH!” to something, just say “No” instead.
This message resonated with me and helped me considerably to get what I
really wanted out of the next few months. I knew that what I really
wanted more than anything was to work with incredible people who
inspired and challenged me on a daily basis. I knew then and still do
now that if this basic need wasn’t met, I’d be looking for a new job
within a few months.
I knew that to make the best of my time, I had to take my time. I put
the word out to the world (via a blog
post) stating what I wanted
and began networking and contacting companies I thought I’d like.
One of the first things I did when I got on the phone with any potential
employer was to tell them that I would not be accepting any jobs
immediately. I wanted to take at least a few months to get a good feel
for the work places and the people of which they were comprised.
I was contacted by nine fantastic opportunities across the world. They
included opportunities in Sweden, Spain, Mexico, the USA, Canada, and my
home, Australia.
With each opportunity, I stated that I wanted to come to their office
(anywhere in the world) and spend a week working with the people who
would become my coworkers and on the projects that I would be a part of.
After several experiences at different locations, I had the pleasure of
spending a week in Chicago with the Obtivians. Finally, this was a place
I knew I wanted to work at. Obtiva has some of the most amazing people
I’ve had the pleasure of getting to know. Each of them is smart,
passionate, interesting, modest, and inspiring. I knew I could only come
out the other end of working with them a better developer and human
being than before.
Several of my visits with offices prior to Obtiva felt like interesting
and challenging places to work at, but none of them gave me an
overwhelming sensation of happiness and excitement as Obtiva did and
still does.
Ultimately, I don’t plan on being a consultant for most of my career,
but I know that as long as I am one, I want to be one with these guys.
So, face up to what you want. Say no until you are moved to say yes as
though by an external force (other than
coercion!). There's no room for compromising on happiness, fulfillment, and personal growth.
Also, Obtiva kicks ass. If you are a passionate developer or have an interesting project, drop us a line, and let us move you to say "HELL YES!"
Pat Allan just had some trouble with his laptop and he lost a bunch of
data.
After he pinged me asking where ghost stored it’s hostname entries so he
could restore them, I realised I didn’t know. Sure, on Linux, ghost just
writes out to the /etc/hosts file, but on OS X, it uses the dscl
command to get to the Directory Services.
Long story short, if you ever want to know where and how Directory
Services stores a lot of it’s information, it’s mostly in the
/var/db/dslocal/nodes/Default/ directory.
Specifically, the hosts are stored in
/var/db/dslocal/nodes/Default/hosts/ directory, one .plist file per
host. Pat reports that just grabbing those files from a his backup and
dropping them in that directory worked wonders.
Want to know how to raise a 500 error in any version of rails (so far)?
Simply add ?a&a[] to the end of any URL in any Rails application.
I tested this on a number of known rails sites such as Chargify,
Twitter, Groupon, Hoptoad (oh the irony), and even a Rails v1 website
somebody managed to find.
For such an old bug (essentially as old as it can be), it’s quite funny
that it was apparently patched less than two weeks
ago.
Continuing what I started with my post last
light,
I’ve got another tip for developing productively when using Cucumber.
I’m going to show you a simple way to pause cucumber stories in order to
investigate the current state of your application at that point.
AfterStep('@pause') do
print "Press Return to continue"
STDIN.getc
end
Then just tag any Cucumber feature or scenario with @pause. After the
first step is run, the scenario will stop running and you can do
whatever you like before proceeding.
My favourite tactic is to combine the @pause tag with @selenium tag
to have WebDriver stop driving FireFox for a moment and let me click
around in the application or inspect the HTML/CSS at that point.
My next post will be about enabling FireBug in the "WebDriver" profile
to help inspecting the web page when you’ve paused a scenario.
I’ve been using Cucumber since pretty much the first day I heard about
it. I’ve worked on a lot of projects that have relied on it’s presence
for reliable development. Therefore, I’ve put a lot of effort into
perfecting my Cucumber infrastructure to make this fantastic tool even
better. I’m going to share one such morsel of code that makes developing
with Cucumber even greater.
The Problem
I’ve worked on a lot of web applications and, as I’m sure many of you
know, quite often the development of a web application is focussed on
the functionality foremost, and the interface and style is incorporated
later. It may be that the client doesn’t yet know the feel they want for
their project or that they want to focus their budget towards
prototyping the application first.
This is fine, except for the fact that changing the HTML and CSS of a
web application after a lot of functionality has been developed is a
fantastic way to break all your integration tests.
This is particularly true if you have scenarios like the following
contrived one:
When I fill in "Username" with "bjeanes" within ".main-panel form#signup-form"
And I press "Sign up!" within ".main-panel form#signup-form"
Then I should see "You have successfully signed up" within ".flash.notice"
The problem is, of course, that designers might change the HTML that
used to be .main-panel form#signup-form into something sexier and more
semantic.
The Solution
This problem is not unlike an already-solved one; we’ve all moved away
from hardcoding URLs like "/users/new" into our views and Cucumber
scenarios and replacing them with new_user_path and the signup page,
respectively.
So why not apply the same formula that paths.rb uses for removing URLs
from scenarios to our situation with selectors?
Here’s what I add to all new projects:
# I'm in features/step_definitions/web_ext_steps.rb
When /^(.*) within ([^:"]+)$/ do |step, scope|
with_scope(selector_for(scope)) do
When step
end
end
# Multi-line version of above
When /^(.*) within ([^:"]+):$/ do |step, scope, table_or_string|
with_scope(selector_for(scope)) do
When "#{step}:", table_or_string
end
end
And:
# I'm in features/support/selectors.rb
module HtmlSelectorsHelper
def selector_for(scope)
case scope
when /the body/
"html > body"
else
raise "Can't find mapping from \"#{scope}\" to a selector.\n" +
"Now, go and add a mapping in #{__FILE__}"
end
end
end
World(HtmlSelectorsHelper)
Applying the Solution
My previous example of the flawed Cucumber scenario now becomes:
When I fill in "Username" with "bjeanes" within the sign up form
And I press "Sign up!" within the sign up form
Then I should see "You have successfully signed up" within the notice flash
And the selectors.rb case statement gets the following additions:
case
# ...
when /the sign up form/
".main-panel form#signup-form"
when /the (notice|error|info) flash/
".flash.#{$1}"
# ...
end
Notice how the scenario now identifies things on our page by their
semantic identifiers, not by brittle CSS or XPath locations which are
prone to change. As a bonus, now if when they do change, the paths only need to be updated in a single
location in our Cucumber test suite!
Patching Cucumber
I feel pretty strongly that CSS and XPath don’t belong in our feature
files because not only does it encourage brittle tests (as shown above),
but also because those selectors are entirely irrelevant to end users,
and that’s kind of the main point of using a natural language DSL to
describe our integration tests, i.e. putting on the user shoes.
I’d really like to patch this back into Cucumber, and I entirely plan to
do so, providing I get the time.
I got the time, and here is my pull
request to have
it merged.
Expanding the Solution
You’ll note my HtmlSelectorsHelper module only accommodates CSS
selectors. That’s only because I have never needed XPath in this
context. It’d be very simple to modify my examples to do so, though,
with a combination of multiple return values and a splat. That’s an
exercise for the reader (or me if I end up patching Cucumber).
Final Word
I apologise for the length of this article, but I congratulate you for
making it all the way through it!
I now have so many blog post ideas lined up that I’ve had to create a
new category in Things.app just to hold them all. This means that I’ll
be striving to get a few more posts done and out the door in the next
few weeks, including a performance comparison of different data
encapsulations for web application APIs on the iPhone (i.e. is it better
to use Plists, JSON, or XML?) and a post on why I think there should be
8 RESTful actions, not the 7 that Rails prescribes by default.
I’ve needed to refer to this fantastic article a number of times and
have always had trouble finding the link, so I am posting it on my blog
because I think it’s something everyone trying to sell Agile to clients
should read and I know I’ll need it again.
Options to Improve Time To
Profit
Mocra
Two weeks ago, Dr Nic informed me, and my coworkers, that he had
accepted a job with
EngineYard and
was moving to San Francisco within the month. Unfortunately, a
consequence of Nic’s wonderful opportunity is that he had to fire us all
and shutdown Mocra.
Of course, he broke the news to us in a more humane way, and even
encouraged me to take on the Mocra name and start a new company to
continue on without him. I haven’t ruled it out yet, but I am taking
this change as an opportunity to consider moving on in my career and
having some new experiences with fresh and inspiring people.
Over the last two years at Mocra, I’ve helped run the business, managed
the clients, and written awesome code, as well as worked on a slew of
open source contributions and projects,
such as ghost.
Possibilities
A few opportunities and offers have presented themselves already, even
without having put myself out there, as I’m now trying. Some of them
have been quite tempting, and I’ve been exploring them, but some have
been less so.
After reading No more yes. It’s either HELL YEAH! or
no, I’ve decided that I want to try to find
a job that’s will make me say "HELL
YEAH!" and go for it.
If you have a position or an endeavour that you think I might be right
for, please get in contact; I’d love to have a chat about the company,
idea, or your processes, and how we might be able to help each other.
I am looking for work starting from the beginning of September. My
long-term goal is to move out of Brisbane, so relocating is an option
I’d enthusiastically pursue. For a number of reasons, however, I’m tied
to Brisbane until about the end of the year.
Therefore, I’d love to find a company that can accommodate me as a
contractor for the few months until my partner and I are in a position
to move out of Brisbane, at which point I’d be eager to come on as a
full-timer. I am more than happy to come work on-site out of Brisbane
before the New Year for small stints, though.
If you want to know more, I’ll be making my
About page more relevant to my skills,
experiences, and interests over the next day or so. In the mean time,
feel free to email me or call me on
skype or +61412639224 to start a
conversation.
I recently was setting up a new VPS on Linode and I got the following
error when trying to create a new database:
ERROR: new encoding is incompatible with the encoding of the template database
HINT: Use the same encoding as in the template database, or use template0 as template.
This error is related to the locale of the system when Postgres was
installed. If you run the commands in my previous
post before installing
Postgres, you should avoid these errors altogether.
If Postgres has already been installed, however, you can run the
following commands (assuming Postgres version 8.4) to reinitialise the
database with the correct encoding:
pg_dropcluster --stop 8.4 main
pg_createcluster --start -e UTF-8 8.4 main
Note: this will erase all databases and reset your postgres
configuration so it is only really useful when you are first setting up
your database, or have taken appropriate measures to be able to restore
your data.