Showing posts with label Testing. Show all posts
Showing posts with label Testing. Show all posts

Monday, March 23, 2015

undefined method `capture_haml' for RSpec

If you're using Haml in a Rails project, testing with RSpec, you might encounter the "undefined method 'capture_haml' for ..." error while trying to run your specs. I encountered this error in a view spec when trying to use Simple Form along with Haml. Fortunately, like many other obscure bugs there is a quick and dirty solution: The init_haml_helpers section is necessary to prevent other errors from happening.

Note that all lines are required. If you drop the init_haml_helpers line, you'll get the following error:
ActionView::Template::Error: undefined method `capture_position=' for nil:NilClass
and of course if you don't have any of this, you will get:
ActionView::Template::Error: undefined method `capture_haml' for #<RSpec::ExampleGroups::...

Thursday, March 19, 2015

Using save_and_open_page with Cucumber

Capybara has a nice utility method called save_and_open_page that will save the current page's HTML as a temp file and open it in your browser. This method is available by default in Ruby code (for example in an RSpec feature spec). As Cucumber is not direct Ruby, you can't directly say that. Fortunately, you can say

 Then show me the page  

and you'll get the same effect.

Monday, March 2, 2015

AssetFilteredError: Assets filtered out and will not be served

If you've decided to use the Teaspoon gem for running your JS tests, you may have encountered this error when trying to navigate to your test suite:
 Error: Sprockets::Rails::Helper::AssetFilteredError: Asset filtered out and will not be served:   
 add `Rails.application.config.assets.precompile += %w( teaspoon.css )` to `config/initializers/assets.rb` and restart your server  
Unfortunately, this is an issue with Teaspoon and newer (>4.0) versions of Rails. For some reason, the asset paths for Teaspoon are no longer included in the list of assets that are automatically precompiled. Therefore, you must add them to your config/initializers/assets.rb file like so:
 unless Rails.env.production?  
  Rails.application.config.assets.precompile += %w(  
   teaspoon.css  
   teaspoon-teaspoon.js  
   teaspoon-mocha.js  
   # other assets  
  )  
Note: I added a surrounding check for production. Since Teaspoon is a testing gem, you shouldn't be using it in production, nor should you need its assets. If you have assets in this file that you need for production, you can separately add them with another addition to Rails.application.config.assets.precompile.

Friday, February 27, 2015

Stubbing Global Methods in JS Specs with Sinon

In your Rails app, your JS should be tested too! Setting that up is a topic for another post. When testing your JS, a lot of the same philosophies apply. One of those is mocking methods other than the one you're testing. How do you do this for methods in other libraries, such as vanilla JS or jQuery?

Vanilla JS (the confirm method):
 sinon.stub(window, 'confirm')  
jQuery (the fadeOut method):
 sinon.stub(jQuery.prototype, 'fadeOut')  

Monday, January 26, 2015

Testing Controllers in Rails Engines

Testing your controllers in a Rails engine requires just a few extra steps compared to testing in a Rails application.

For example, you're working on an engine called Animals, and you have a BearsController with an index action that returns all the bears you've registered.

With a controller like this:
 // app/controllers/animals/bears_controller.rb  
 module Animals  
   class BearsController < ApplicationController  
     def index  
       // return all bears  
     end  
   end  
 end  
and a routes file like this:
 // config/routes.rb  
 Animals::Engine.routes.draw do  
   get '/index' => 'bears#index'  
 end  
you will need to update your dummy routes file to look like this:
 // spec/dummy/config/routes.rb  
 Rails.application.routes.draw do  
   mount Animals::Engine => "/animals"  
 end  
and in your controller spec you will need to add one extra line compared to normal:
 // spec/controllers/animals/bears_controller_spec.rb  
 RSpec.describe BearsController do  
   // this is the key line!  
   routes { Animals::Engine.routes }
  
   describe 'GET :index' do  
     it 'does the thing' do  
       get :index
       // test that all bears are returned  
     end  
   end  
 end  
and now your controller test will go to the appropriate routes :)

Monday, January 19, 2015

Testing Loaded Devise Modules In Your Models

So you're using Devise or devise_token_auth for user authentication in your Rails project. Awesome! Both are great libraries for authentication. Now, how are you testing your code? You probably have feature tests to ensure tasks like creating a new user work (and if you don't, stop reading this right now and make them!). You might even have controller tests to ensure, for example, that each action authenticates the user before proceeding.

What about your models? They are important too, and fortunately these libraries handle most of the model work in the background for you. There are still things you can test though! The main thing that come to mind are ensuring that you only load the devise modules that you want. For devise_token_auth, you also want to ensure you load the model concern.

Devise Modules

Let's say that you're only using the Database Authenticateable and Registerable modules. With Devise, your model might look something like this:

 class User < ActiveRecord::Base  
    devise :database_authenticatable, :registerable  
 end  

Devise provides a method called devise_modules that returns an array of symbols representing the modules that you include. With this, you can easily test that you're using only these two modules by doing this:

 // spec/models/user_spec.rb  
 // using RSpec and FactoryGirl for testing  
 RSpec.describe User do  
    it 'has the right devise modules loaded' do  
       user = FactoryGirl.build(:user)  
       expect(user.devise_modules).to eq([:database_authenticatable, :registerable])  
    end  
 end 

What if there was a business requirement that Omniauth was not available? Well, you can test that easily too:

 it 'does not load the Omniauth module' do  
   user = FactoryGirl.build(:user)  
   expect(user.devise_modules.include?(:omniauthable)).to be_falsey  
 end  

DeviseTokenAuth concern

With devise_token_auth, you also include a concern to add extra methods to User that are used internally. How can you test this? Easily!

 it 'loads the DeviseTokenAuth concern' do  
   expect(User.ancestors.include?(DeviseTokenAuth::Concerns::User)).to be_truthy  
 end  

Summary

With these tests, you can be sure that your model includes everything from Devise that you're expecting it too. This is great insurance for when you decide to refactor your model 6 months from now. It makes your assumptions clear, ensures that any changes are explicit, and allows you to test your user model is using Devise without having to run your expensive feature specs.