Blog
what did i learn today
Technology ruby on rails rspec
[RSPEC] Handling the MockExpectionError "does not implement"

I was writing a test for my helper which would, if the user is authorised to perform an action, show the actual link, and otherwise show the title or nothing (depending on what is wanted).

I make use of the vigilante gem which I should still write an article about, but this gem allows to store authorisations in the database and can be context-specific (e.g. you you can be assigned different roles for different "contexts", which could translate to organisations, projects, ... whatever applies to your setup). This gem adds a helper method is_allowed_to? to determine if one has the permissions to perform a certain given action.

When I wanted to mock this in my helper, I assumed a simple

     expect(helper).to receive(:is_allowed_to?) { true }

would suffice. However, I got a very strange error:

RSpec::Mocks::MockExpectationError: #<#<Class:0x00007fe2dc84e010>:0x00007fe29c83dc78 ....snip some very long things ... >> does not implement: is_allowed_to?

This is actually a great feature from Rspec: it checks if the thing you want to mock/stub actually exists on the original object, but in this case a helper (or a view) in Rails can access a lot of helper methods which are implicitly loaded.

This behaviour can be controller by setting the RSpec::Mocks.configuration.verify_partial_doubles to false. Of course I do not want to disable this for my entire test-suite, but just locally for the single test or single spec file.

So in my spec I temporarily disable the checking for the existence of doubles, as follows

require 'rails_helper'

RSpec.describe MapHelper do
  before(:all) do
    RSpec::Mocks.configuration.verify_partial_doubles = false
  end

  after(:all) do
    RSpec::Mocks.configuration.verify_partial_doubles = true
  end

  it "has the correct method" do
    expect(helper.respond_to?(:map_checked_link_to)).to eq(true)
  end

  context "map_checked_link_to" do
    context "a reachable feature" do
      before do
        @pipe = FactoryBot.create(:pipe)
      end
      it "renders a normal link" do
        allow(helper).to receive(:is_allowed_to?).and_return("XXX00")
        expect(helper.map_checked_link_to("Pijpstuk 123", @pipe, {})).to eq("<a href=\"/pipes/208\">Pijpstuk 123</a>")
      end
    end
    context "a blocked feature" do
      before do
      end
      it "does not render anything by default" do
        allow(helper).to receive(:is_allowed_to?) { false }
        expect(helper.map_checked_link_to("Pijpstuk 123", "", {})).to eq("Pijpstuk 123")
      end
      it "renders the title if so specified" do
        allow(helper).to receive(:is_allowed_to?) { false }
        expect(helper.map_checked_link_to("Pijpstuk 123", "", {}, true)).to eq("Pijpstuk 123")
      end
    end
  end

end

I also stumbled upon a PR implementing a method without_verifying_partial_doubles which takes a block which would do exactly the same. So one would be able to write

it "does something weird with mocks" do
  without_verifying_partial_doubles do
    ...
  end
end

But it did not work for me. Not sure if that is because the rspec version I am using in this project is too old, or the example I found was outdated.

More ...
Technology ruby on rails rspec
[RSPEC] Cleaning up orphaned attachments when running specs

So when running the specs we also create a lot of fake attachments, but they are never cleaned up. Which is probably obvious, because we never actually destroy the models (containing the attachments), but truncate the database or rollback the transactions.

So I tried to find a way to 1) automatically/more easily clean up those dummy attachments, and 2) make sure it works when using parallel:specs. And over my different projects, where in some I use different gems to manage my attachments.

In one project, I am using paperclip and there I took the following approach. In the initializer config/initializers/paperclip.rb I wrote

  Paperclip::UriAdapter.register
  if Rails.env.production?
    Paperclip::Attachment.default_options.merge!(
      hash_secret: ENV.fetch('SECRET_KEY_BASE'),
      s3_protocol: :https,
      url: ':s3_domain_url',
      path: "/:class/:attachment/:id/:style/:hash.:extension",
      storage: :s3,
      s3_credentials: { .. }
    )
  elsif Rails.env.development?
    Paperclip::Attachment.default_options.merge!(
      url: "/system/:class/:attachment/:id/:style/:hash.:extension",
      hash_secret: Rails.application.credentials.secret_key_base
    )
  elsif Rails.env.test? || Rails.env.cucumber?
    Paperclip::Attachment.default_options.merge!(
      url: "/spec_#{ENV['TEST_ENV_NUMBER']}/:class/:attachment/:id/:style/:hash.:extension",
      hash_secret: Rails.application.credentials.secret_key_base
    )
  end

and then in rspec rails_helper.rb I can add the following piece of code

  config.after(:suite) do
    FileUtils.remove_dir(File.join(Rails.root, 'public', "spec_#{ENV['TEST_ENV_NUMBER']}"), true)
  end

In another projects I am using carrier_wave and there it is a little more difficult, but it amounts to the same approach. In CarrierWave we create different uploaders, and each have their own configuration. In my project I first iterate over all uploaders in my own code-base, and explicitly require one uploader from our own shared gem (between different projects). So we add an initializer config/carrierwave_clean_spec_attachments.rb (or whatever name you prefer) to override the path when in test mode:

if Rails.env.test? || Rails.env.cucumber?
  Dir["#{Rails.root}/app/uploaders/*.rb"].each { |file| require file }
  require 'document_uploader'

  CarrierWave::Uploader::Base.descendants.each do |klass|
    next if klass.anonymous?
    klass.class_eval do
      def cache_dir
        "#{Rails.root}/spec/support/uploads_#{ENV['TEST_ENV_NUMBER']}/tmp"
      end

      def store_dir
        "#{Rails.root}/spec/support/uploads_#{ENV['TEST_ENV_NUMBER']}/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
      end
    end
  end
end

and then in my rails_helper.rb I can then add the following statement:

config.after(:suite) do
  FileUtils.rm_rf(Dir["#{Rails.root}/spec/support/uploads_#{ENV['TEST_ENV_NUMBER']}"])
end

How do you do this? Do you use another gem for storage/attachments and how do you solve it? E.g. when using ActiveStorage ?

More ...
News content_for view testing yield rails rspec
[rspec] view testing: testing the content_for results

I normally do not do a lot of view specs, but at least I want to make sure that my view renders without errors. And sometimes I really need to make sure that some link is shown or hidden depending on e.g. the role of the user or linked objects.

For example,

 describe "posts/show.html.haml" do
   context "without any comments" do
     it "displays no comments" do
       @post = Factory(:post)
       render
       rendered.should contain(I18n.t('posts.show.no_comments'))
     end
   end
 end

So we check the rendered result, whether it contains a specific text.

But what happens if your view is rendering different yield regions? Like a body (the default region) and a sidebar.

Let's use a view like this :

  = show_for @post do |p|
    = p.attribute :title 
    = p.attribute :content
  - if @post.comments
    = render :partial => 'comments' 
  - else 
    = t('posts.show.no_comments')
 
  = content_for :sidebar do
    = link_to 'Edit', edit_post(@post) if is_allowed_to?(:edit)

It renders the attributes using the show_for gem, and then renders inside the sidebar a link if the current user is allowed to edit it.

Now I want to test what is rendered into the sidebar. To my dismay I found that neither content_for or content_for?worked at all inside rspec.

And rendered does not contain the data for the other regions.

So somehow I would want to get to the content for :sidebar.

It appears that the different regions are actually stored inside an instance variable of the view. Once I figured that out, the rest was easy:

  describe "posts/show.html.haml" do
    def rendered_content_for(name)
      view.instance_variable_get(:@_content_for)[name] 
    end 
    
    context "with enough rights" do
      it "displays a link to edit the post" do
        @post = Factory(:post)
        view.stub(:is_allowed_to?) { true }
        render
        rendered_content_for(:sidebar).should contain('Edit') 
      end
    end 
    context "with no rights" do
      it "does not display a link to edit the post" do
        @post = Factory(:post)
        view.stub(:is_allowed_to?) { false }
        render rendered_content_for(:sidebar).should_not contain('Edit')
      end
    end
  end

Hope that this helps somebody. Or did you find a better way?

More ...
News arrrrcamp fast tests rails rspec
Rethinking your design: slim models, fat presenters?

For me, it started with a tweet from Uncle Bob Martin, saying that if all your domain logic is in your model, or if you put your domain logic inside your models by default, then you are doing it wrong. I think the reasoning behind this is that we should design the domain model before relying on our database model (which is actually an implementation of that domain model). Because the best way to store something in the database is not always to best way to treat or represent or handle the data. In my Rails projects, I have to admit that in most cases the database is my domain model. Which in simple cases is correct, but in more complex cases it is not anymore. Secondly what happens is that your database model could shine through in your UI, or if it does not, your views or helpers could get very heavy. One good way to handle this is to use Presenters. Which goes into the direction of that domain logic, independent of the database. A Presenter is a class that groups all data, knows where to find it, or store it, and will match one-on-one on your presentation/view logic. Thirdly, I went to arrrrcamp, and there was Corey Haines doing his talk about Fast Rails Tests. While this title was very promising, Corey warned us that, in his own words, we would be underwhelmed. This was not entirely true, but not entirely false either :) In short what Corey said was: testing in Rails is slow because we need to drag around the framework, so why not cut out the framework where possible. So he showed an example where he extracted code from models and into separate modules, where you test them standalone. Standalone means: without requiring @spec_helper@. I did this for our modules inside @lib@ where possible, and truth be told: those specs now really fly! Awesome. But the large parts of our test-suite is our models, controllers, views, helpers where this is not possible nor wanted. I want to be able to run our complete test-suite faster, and this was not a solution for that. We scraped a few seconds of our complete time. Extracting code from our models into standalone modules will not make our total test-suite go faster either. Still it is something well worth investigating further. I don't believe in splitting up classes into modules just for the sake of making my tests faster. There has to be some logical reason (pertaining to the domain model --that is). But maybe presenters could be the way out here:

  • they group domain logic
  • while they are responsible for retrieving the correct objects from the database, these actions are just delegated to the responsible models So that sounds promising to me and a road I will investigate.
More ...
Uncategorized ruby rspec
awesome rspec hints and tips

Read "Pure Rspec", a great slideshow by Jon "Lark" Larkowski. It contains some really cool rspec tips and hints, how to write your tests more readable and compact.

More ...
Uncategorized view testing rspec2 rails3 rails rspec
rails3 and rspec2 view testing

Testing the view in Rspec is just great. It allows you to test the view in total isolation. It just does the render. Make sure you set up some data that is needed, and then check if all areas are available for instance. Now for Rspec2 a lot of things have changed:

  • have_tag is no longer supported: use have_selector from webrat instead
  • response is deprecated, use rendered instead
  • the assignment has changed, write assign(:events, [stub_model(Event)]) instead of using the old assigns[:events]=[...] notation Let me show you a full example, because that always makes things clearer: [ruby wraplines="false"] require 'spec_helper' DUMMY_NUMBER="dummy number" DUMMY_MESSAGE="dummy message" describe "envelopes/index.html.haml" do before (:each) do envelope = mock_model(Envelope) envelope.should_receive(:destination).and_return(DUMMY_NUMBER) envelope.should_receive(:message).and_return(DUMMY_MESSAGE) assign(:envelopes, [envelope]) render end it ("has a h1") { rendered.should have_selector('h1')} it ("shows the correct title") { rendered.should contain(t('envelopes.list.title')) } it ("have a table-grid") { rendered.should have_selector('.grid') } it ("shows the destination") { rendered.should contain(DUMMY_NUMBER) } it ("shows the message") { rendered.should contain(DUMMY_MESSAGE) } end [/ruby]
More ...
Uncategorized refactoring rspec
rspec testing using macros

[ruby wraplines="false"] STATUS_EXPECTED_TRANSLATIONS = [[inp1, outp1], ... ] it "should translate received statuses" do STATUS_EXPECTED_TRANSLATIONS.each do |tr| controller.send('translate_status_to_envelope_status', tr[0]).should == tr[1] end end [/ruby] While this is short and DRY, a problem arises when one of the values in my look-up table does not behave as expected anymore. The complete test will fail, without having any clue which value caused it to fail. That is not good! But luckily, in rspec there exists the option to use macros. It is a kind of templates for tests. Using macros i could write this much cleaner, and generate my test-clauses on the fly. [ruby wraplines="false"] STATUS_EXPECTED_TRANSLATIONS.each do |tr| it "should translate #tr[0]} to success" do controller.send('translate_status_to_envelope_status', tr[0]).should == tr[1] end end [/ruby] Now if a test fails, i know which i value need to check, because i will get an appropriate error-message.

More ...
Uncategorized ruby on rails rspec
skipping slow tests in rspec2

For Test::Unit there is a module called skippy, that allows you to skip for instance slow tests. I was looking for a way to achieve the same in Rspec. I am currently developing against Rails 3.0.0.rc, and using rspec 2.0.0.beta.19 and could not find anything. So i asked on the rspec mailing list and got a great answer! It is actually present in rspec2, right out of the box, as a feature they needed for their own tests. Read the description by David himself here. He shows a lot of very good examples, even filtering tests based on ruby-version is now possible. Awesome. In short, you would do something like : [ruby] describe "something", :slow => true do it "should be blabla" end describe "something else" do it "should be a slow test", :slow => true do ... end it "should be a fast test" do .. end end [/ruby] then you can write the following to skip the slow tests : [ruby] # in spec/spec_helper.rb RSpec.configure do |c| c.filter_run_excluding :slow => true end [/ruby] and to run only the slow tests: [ruby] # in spec/spec_helper.rb RSpec.configure do |c| c.filter_run :slow => true end [/ruby] and to filter on slow tests, and run all tests if no slow tests are available: [ruby] # in spec/spec_helper.rb RSpec.configure do |c| c.filter_run :slow => true c.run_all_when_everything_filtered = true end [/ruby] Awesome! :) For the moment there is no command-line support, but in the spec/spec_helper.rb you could also check for any environment variables to do the slow or fast test-set. What i personally needed, was something that would either run all tests, or run all tests excluding a few (i have a test-suite for an sms-gateway and do not want to send sms-es all the time). So i handled that like this: [ruby] # in spec/spec_helper.rb RSpec.configure do |config| if ENV['SEND_REAL_SMS'] != "true" config.filter_run_excluding :send_sms => true end end [/ruby]

More ...
Uncategorized ruby win32 cucumber ruby on rails rspec
missing 'a' with rspec and cucumber

I am developing Ruby on Rails on Windows platforms mostly. But using Rspec and cucumber on windows has a very strange side-effect: all a's are missing as can be seen from the following screenshot: rspec-without-a-smallerLuckily, after some very extensive googling, i found a single blogpost with a fix! Apparently it has something to do with UTF-8 encoding, and the simple solution is that you need to change the encoding of the current command prompt. This can be achieved via a simple call before you start: [sourcecode] chcp 1252 [/sourcecode] The aforementioned post then proposes to adjust the cucumber.bat to not have to type this every time. This is all good for cucumber, but not for rspec, and anyhow, every new update of the cucumber i would need to apply this fix again. I was thinking that it might be possible to set the default codepage, which is 850 on my machine, to 1252 permanently. As this blogpost mentions, there are two different ways to achieve the wanted result.

Change the codepage system-wide

This can be done in the registry. [sourcecode] [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage] OEMCP=1252 [/sourcecode] But one commenter notes this is not without risk.

Only for command prompt

An alternative way is to put make sure that each time a console is opened (cmd.exe) the codepage is set automatically. [sourcecode] [HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor] Autorun=chcp 1252 [/sourcecode] This will only work for console windows in which you run cmd.exe, which is just what i needed.

More ...