Posted by oandreev on March 27, 2007
Have you ever forgotten to setup required fixtures in spec because of their quantity? Sometimes we have up to 10..20 tables to be filled before spec runs. In opposite, you may write many tiny contexts, so that you need short fixtures definitions in each of them. But the latter leads to very unmaintainable un-DRY code, especially when you add new entity to the project which touches many specs.
One may consider make all fixtures global, so that you don’t need to use fixtures method at all. But having 500 specify blocks and loading 30 tables per each isn’t that fast.
What we need is auto-loaded fixtures. Once model Person is mentioned in specify block, fixture people is loaded. That strategy guarantees three things:
1. Models always get required data in database
2. We don’t spend time on loading non-used fixtures
3. We don’t have to manage one more dependency, which is not that cool at all.
The main difficulty here is how catch the moment when model class appers in specify do .. end. Each constant gets catched and autoloaded by active_support/dependencies, but it is done only once, so it is not the right place to fixturize :) database.
One of the possible solutions is to hide ActiveRecord model constants before specify so to catch them in const_missing. Constants are collected within AR::inherited and loaded or revealed in const_missing.
The main problem now is to figure out which evaluation context asks for constant. This is needed for calling fixtures in the right place. You may ask: why don’t you just add const_missing to every context object to catch constant exactly where fixture is to be loaded. I tried that. RSpec uses blocks for specs, and ruby blocks, as everybody knows, are pure closures. Constant mentioned in block context have no way to be searched for in block caller context. Even using instance_eval or eval with binding (that’s nearly the same as instance_eval in that case).
Another problem is to catch fixture shortcuts: like people(:oleganza). Person class may not be even loaded before such call, so we need to do some simple heuristics to load class and fixtures altogether.
Of course, instantiated fixtures (@people) are not supported. They are slow and nobody uses them anyway.
I’m still in search for working solution. Hope it will be nice. Stay tuned, more to come.
Tagged with: fixtures rspec |
Posted by yrashk on March 06, 2007
I’ve started playing with JRuby 0.9.8. I personally think that it has great potential for web development in Rails (they have 98% of rails tests passing already !).
What I was pleased to see is that (thanks to Java) unicode is working in JRuby just fine without any workarounds like String#chars :

That’s very, very good! Continuing to explore JRuby possibilities.
Tagged with: java jruby ruby unicode |
Posted by yrashk on March 02, 2007
I was talking about generated spec names few hours ago. Although, I’ve found that they simply don’t work properly with “be” matchers. I’ve tried to produce a quick workaround for this. That’s what I’ve created:
gem 'activesupport'
require 'active_support'
class Spec::Matchers::Be
def description
"be #{@comparison}#{@expected} #{@args.to_sentence}"
end
end
Now I can run following code:
context "Random number in 0..100 range" do
setup do
@random_number = rand(100)
end
specify do
@random_number.should be < 100
end
specify do
@random_number.should be >= 0
end
specify do
@random_number.should_not be_nil
end
specify do
@random_number.should be_between(0,100)
end
specify do
@random_number.should_not be_between(100,200)
end
end
and get this as output:
Random number in 0..100 range
- should be < 100
- should be >= 0
- should not be nil
- should be between 0 and 100
- should not be between 100 and 200
Update: This improvement was included into rspec trunk
Tagged with: rails rspec ruby |
Posted by yrashk on March 02, 2007
What I also like in RSpec is that it constructs a name for simplistics specifications, like:
context "Random number in 0..100 range" do
setup do
@random_number = rand(100)
end
specify do
@random_number.should < 100
end
specify do
@random_number.should >= 0
end
specify do
@random_number.should_not be_nil
end
end
Random number in 0..100 range
- should < 100
- should >= 0
- should not be nil
Tagged with: rspec |
Posted by mklishin on March 02, 2007
Ruby developers beloved BDD framework
RSpec goes 0.8.1. This is actually a bugfix release after 0.8 one day earlier. It has new expectation syntax although old one is here to stay up to 0.9. But what is much more interesting, now we have a way to
add our own expectations and matchers following simple methods overriding rules.
class BeInZone
def initialize(expected)
@expected = expected
end
def matches?(actual)
@actual = actual
bob.current_zone.eql?(Zone.new(@expected))
end
def failure_message
"expected #{@actual.inspect} to be in Zone #{@expected}"
end
def negative_failure_message
"expected #{@actual.inspect} not to be in Zone #{@expected}"
end
end
def be_in_zone(expected)
BeInZone.new(expected)
end
and use it like this:
module CustomGameMatchers
class BeInZone
...
end
def be_in_zone(expected)
...
end
end
context "Player behaviour" do
include CustomGameMatchers
...
end
Given Ruby DSL power this may lead to some unpredictable addiction to BDD and Ruby in general.
Don't forget to run
script/generate rspec
as you're done with installation of Rails plugin from tag 0.8.1 cause it generates one more file for run preferences. I said preferences? Yes, like colored output. Passed specs are now green in Bash, messages are purple and failures are red. Really easy to recognize after months of black and white.
The same day Mauricio Fernandez posted announcement of RCov
0.8, code coverage tool for Ruby that works with RSpec. Not that bad...
And don't forget to read new documentation to RSpec, a lot of stuff changed there but everything is still easy to learn and specs look even closer to human written requirements text. What else could we ask BDD framework of?
Tagged with: bdd rcov rspec tools |