RSpec and HAML Helpers
Today I faced a problem with specing a helper where I use the haml_tag/puts methods (haml_tag was previously open).
Here’s a solution working with Rails 2.0.2, RSpec 1.0.3 and HAML 1.8.2 :
in spec_helper.rb add :
Spec::Runner.configure do |config|
...
# Activate haml to spec helpers
config.with_options(:behaviour_type => :helpers) do |config|
config.include Haml::Helpers
config.include ActionView::Helpers
config.prepend_before :all do
# Update from Evgeny comment, with HAML >= 1.8.2, you can call
init_haml_helpers
# Old way for HAML <= 1.8.2
# @haml_is_haml = true
# @haml_stack = [Haml::Buffer.new]
end
end
...
endthen you can write your helper spec like this :
describe ApplicationHelper do
describe "#top_navigation_menu when logged in" do
it "returns the menu" do
@user = mock_model(User)
capture_haml {
top_navigation_menu(@user)
}.should_not be_empty
end
endThe interesting method is capture_haml, which does the same as Rails builtin capture but for HAML.
haml_tag/puts methods write output directly to the buffer and does not return the generated content as a String, thus we cannot just test on the method output.[…]
RSpec 1.1
David Chelimsky anounced this morning the release of RSpec 1.1 (as of now the website is not up to date) RSpec 1.1 and just now the brand new website hosting.
What’s in this release ?
- StoryRunner : merge from Dan North ’s RBehave library. It aimed at providing a clean way to express and automate Acceptance Tests.
- Nested Example Groups : allows you to nest your
describeblocks resulting in group sharing common specifications.
- Support for Rails 2.0.1
- Test::Unit interoperability : switch smoothly from Test::Unit to RSpec by allowing you to run your Test::Unit tests with RSpec. The goal here is to provide a way to progressively transition your tests to RSpec syntax.
More infos on David blog post about the RSpec 1.1 release
Congratulation and many thanks to everyone involved in this release ![…]
RSpec and Inline RJS
Rails give us the ability to write inline RJS via render :update syntax, as in :
render :update do |page|
page['addressPreviewStatus'].update 'Address Not Found'
endPrevious code will update the content of tag with “addressPreviewStatus” id with ‘Address Not Found’.
But how can we spec that out ? I needed to search a little as there seems to be very little examples.
First in rails there is a special assertion assert_select_rjs, merged from the assert_select plugin, it let you test your RJS with a syntax similar to RJS itself.
RSpecOnRails has a special matcher wrapping assert_select_rjs : has_rjs.
You can use it on response to specify what should be generated, for exemple you can :
# Specify response should contains an update or insert of some kind
response.should have_rjs
# Specify response should contains an update or insert for the tag with given id
response.should have_rjs('id')
# Specify response should contains a specific update, insert, etc. for the given tag
response.should have_rjs(:replace, 'id')You get the point. Now, a nice syntax allows you to write RJS this way :
render :update do |page|
page['id'].update('replacement text')
endSo my first try was to use :
response.should have_rjs(:update, 'id', 'replacement text')but this fail miserably with an error Unknown RJS statement type update. I tried different syntax but none worked.
Finally browsing through source of assert_select_rjs I found what I was looking for. When using this syntax you should use one of :chained_replace or :chained_replace_html depending what you want to test :replace or :update.
Now here is the solution :
response.should have_rjs(:chained_replace_html, 'id', 'replacement text')RSpec On Rails and HAML
As my incursion into RSpec continue, I encountered a strange problem on my CC machine. I switched my default layout (aka application.rhtml) to HAML, so the file became application.haml. No problem on my development machine running specs either with spec command or with rake. But the CC machine start sending me error in my specs, telling me application.rhtml does not exists.
I searched some looooong minutes which stack generates this error, four possibilities : RoR, RSpec, Gettext, HAML plugin.
The last thing I did before the specs breaks was to switch a controller spec from view isolation to view integration. So I looked more closely to RSpec On Rails plugin and found something interesting, to run in isolation mode, RSpec on rails stub those methods :
@template.stub!(:file_exists?).and_return(true)
@template.should_receive(:render_template).any_number_of_times.and_return(true) { |*args|
@first_render ||= args[2]
}
@template.stub!(:find_template_extension_for).and_return("rhtml")
@template.stub!(:read_template_file).and_return("this is fake content generated by rspec")So it faint Rails about an existing application.rhtml which in fact does not. Rails in its great willingness cache the different template/template extension lookups even in test mode. Then when mixing view integration and view isolation in specs, if those with view integration run after those in isolation, it breaks… or it should, as I can’t reproduce this in my development machine nor can I reproduce this on my CC machine when launching the specs by hand with the spec command (it does occurs only with rake).
About the Rails choice to cache, I fully understand why it is as it is, there should be no template rename during test as it is possible in development mode. So it’s not Rails fault.
But what can we do about this, the simplest solution I came to was to set the template extension caching to off in config/environments/test.rb :
config.action_view.cache_template_extensions = falseHope this helps someone else using RSpecOnRails with a custom template system.
[Update]
The difference between my two machine was revealed to me in just after I post this. The rake task on the CC machine, does not sort the controller spec files as my development machine, the problematic file is called addresses_controller_spec.rb so it was the first one evaluate in my development machine and is drowned between others in my CC machine.[…]
RSpec on Rails : RESTful Authentication
For a new Rail project I’m working on, I decided to try to use RSpec.
The main problem I see with this solution is that not so many plugins out there have RSpec tests and so it can be boring to have to run autotest and rspec_autotest in parralel to be sure both RSpec and Test::Unit tests passes.
Helped by a recent post of Yurii Rashkovskii : RSpec on Rails: acts_as_authenticated , I adapted and add a few specifications to be compatible with restful_authentication plugin.
Here are the files :
- user_spec.rb : User model specs
- users_controller_spec.rb : Users controller specs
- sessions_controller_spec.rb : Sessions controller specs
Just put those in spec/{models,controllers}, copy users.yml fixtures from test/fixtures to spec/fixtures/ and you’re done.
Note I did not spec the UserObserver for now.
[Update]
I refactored a little bit my previous files, add some specs and try to spec user_notifier and user_observer models as well.
- user_spec.rb : User model specs
- user_notifier_spec.rb : UserNotifier model specs
- user_observer_spec.rb : UserObserver model specs
- users_controller_spec.rb : Users controller specs
- sessions_controller_spec.rb : Sessions controller specs
You should also add :
def set_mailer_in_test
ActionMailer::Base.delivery_method = :test
ActionMailer::Base.perform_deliveries = true
ActionMailer::Base.deliveries = []
endto Spec::Rails::EvalContext class in your spec_helper.rb.
I’m waiting for your comments on notifier/observer specs, I didn’t see any examples of them in rspec and I’m not sure of the best way to spec them. For example, UserNotifier is an ActionMailer and resides in models directory, but it acts more like a controller (render views), well you see the point.
[Update]
For those of you who switch to RSpec 1.0.x, Jonathan Linowes has updated my files to make them compatible with the new syntax (thanks). You can grab them at : Rspec 1.0 and Restful Authentication[…]