How to refactor helper methods in RSpec files? - ruby-on-rails

I am using Ruby on Rails 3.1.0 and the rspec-rails 2gem. I would like to refactor the following code (I have intentionally omitted some code and I have given meaningful names in order to highlight the structure):
describe "D1" do
# Helper method
def D1_Method_1
...
end
context "C1" do
# Helper methods
def D1_C1_Method_1
session.should be_nil # Note: I am using the RoR 'session' hash
D1_Method_1 # Note: I am calling the 'D1_Method_1' helper method
...
end
def D1_C1_Method_2
...
end
it "I1" do
D1_Method_1
...
end
it "I2" do
...
D1_C1_Method_1
D1_C1_Method_2
end
end
context "C2" do
# Helper methods
def D1_C2_Method_1
...
end
def D1_C2_Method_2
...
end
it "I1" do
D1_Method_1
...
end
it "I2" do
...
D1_C2_Method_1
D1_C2_Method_2
end
end
end
What can\should I make in order to refactor the above code?
P.S.: I have tried to extract helper methods in an external module (named Sample) but, for example relating to the D1_C1_Method_1 method (that contains the RoR session), I get the following error when I run the spec file:
Failure/Error: session.should be_nil
NameError:
undefined local variable or method `session' for Sample:Module

Have you tried to include the helpers as an external module?
require 'path/to/my_spec_helper'
describe "D1" do
include MySpecHelper
...
end
And now the helper:
# my_spec_helper.rb
module MySpecHelper
def D1_C1_Method_1
session.should be_nil
...
end
end

Related

Rails ActiveSupport::TestCase - How do I dynamically define tests together with their helper methods?

I am building some abstraction into my Rails (5.2) tests as I want to run the same tests multiple times with different parameter sets.
I can successfully create a helper to generate test classes on the fly. This looks like the following, within my test_helper.rb:
class << self
def test_configs(configs: DEFAULT_CONFIGS, &block)
configs.each do |c|
Class.new(ActiveSupport::TestCase) { yield(c) }
end
end
end
I can use this helper in any given test file as follows:
require 'test_helper'
class SampleTest < ActiveSupport::TestCase
test_configs do |c|
test "#{c[:name]} - something is true" do
puts "#{c[:name]}" => # Correctly outputs c[:name]
end
end
end
"#{c[:name]}" is correctly interpolated for each iteration according to what "config" is passed from the helper. So far so good.
I am having though a problem creating some helper methods that also make use of the variable c, either within the test_configs method itself or within single test files.
None of the following works in giving a consistent match between the c variable that is passed to the test titles and what happens within the tests themselves:
# Approach 1
class SampleTest < ActiveSupport::TestCase
test_configs do |c|
def config_name
"#{c[:name]}" # => undefined local variable or method `c'
end
test "#{c[:name]} - something is true" do
puts "#{config_name}"
end
end
end
# Approach 2
class SampleTest < ActiveSupport::TestCase
test_configs do |c|
define_method("config_name") {
"#{c[:name]}"
}
test "#{c[:name]} - something is true" do
puts "#{config_name}" # => Uses only the last definition
end
end
end
# Approach 3
class << self
def test_configs(configs: DEFAULT_CONFIGS, &block)
configs.each do |c|
define_method "config_name" do # (same with def config_name)
"#{c[:name]}"
end
Class.new(ActiveSupport::TestCase) { yield(c) }
end
end
end
# => undefined local variable or method `config_name'
How do I get to correctly "inject" methods which make use of the passed variable?
The right approach was similar to the 3rd one in my question, but yield must be replaced by instance_eval in order for the code within the block to inherit the context of the new class I am creating:
# test_helper.rb
class ActiveSupport::TestCase
class << self
def test_configs(configs: DEFAULT_CONFIGS, &block)
configs.each do |c|
Class.new(ActiveSupport::TestCase) {
define_singleton_method "config_title" do
"#{c[:name]}".capitalize
end
define_method "config_name" do
"#{c[:name]}"
end
instance_eval(&block)
end
end
end
end
end
# sample_test.rb
require 'test_helper'
class SampleTest < ActiveSupport::TestCase
test_configs do
test "#{config_title} - something is true" do # => Correctly invokes the class method defined above
puts "#{config_name}" # => Correctly invokes the instance method defined above
end
end
end
As you can see, there is also a difference in that class methods must be defined in order to be used within test titles, while instance methods must be defined for being used within tests.
In case c must be passed to the block (e.g. for defining additional methods within the block itself), instance_exec should be the way to go: docs.

Is it possible to invoke a helper_method from another Helper?

I have two Helpers, ExamsHelper and ResultsHelper
exams_helper.rb
module ExamsHelper
def get_data
...
end
end
results_helper.rb
module ResultsHelper
def find_result
...
end
end
Is it possible to access the get_data method in ResultsHelper.
I know that if I am declaring it on the ApplicationHelper, I can access it. Is there any other solution for it?
You can always use include:
module ResultsHelper
include ExamsHelper
def find_result
get_data # works
end
end

Stubbing Grape helper

I have Rails app with Grape API.
The interface is done with Backbone and Grape API provides it all data.
All it returns is user-specific stuff, so i need reference to currently logged in user.
Simplified version looks like this:
API initialization:
module MyAPI
class API < Grape::API
format :json
helpers MyAPI::APIHelpers
mount MyAPI::Endpoints::Notes
end
end
Endpoint:
module MyAPI
module Endpoints
class Notes < Grape::API
before do
authenticate!
end
# (...) Api methods
end
end
end
API helper:
module MyAPI::APIHelpers
# #return [User]
def current_user
env['warden'].user
end
def authenticate!
unless current_user
error!('401 Unauthorized', 401)
end
end
end
So, as you can see, i get the current user from Warden and it works fine. But the problem is with testing.
describe MyAPI::Endpoints::Notes do
describe 'GET /notes' do
it 'it renders all notes when no keyword is given' do
Note.expects(:all).returns(#notes)
get '/notes'
it_presents(#notes)
end
end
end
How can I stub helpers's method *current_user* with some specific user?
I tried:
setting env/request, but it doesn't exist before calling get method.
stubbing MyAPI::APIHelpers#current_user method with Mocha
stubbing MyAPI::Endpoints::Notes.any_instance.stub with Mocha
Edit:
At the moment, it's stubbed this way:
spec:
# (...)
before :all do
load 'patches/api_helpers'
#user = STUBBED_USER
end
# (...)
spec/patches/api_helpers.rb:
STUBBED_USER = FactoryGirl.create(:user)
module MyAPI::APIHelpers
def current_user
STUBBED_USER
end
end
But it's definitely not the answer :).
comments mentioned in this issue should help you, It's how even Grape tests it's helpers,
https://github.com/intridea/grape/blob/master/spec/grape/endpoint_spec.rb#L475
(If the code is not there on the same line due to changes, just do a ctrl+f & look for helpers)
Here's some code from the same file
it 'resets all instance variables (except block) between calls' do
subject.helpers do
def memoized
#memoized ||= params[:howdy]
end
end
subject.get('/hello') do
memoized
end
get '/hello?howdy=hey'
last_response.body.should == 'hey'
get '/hello?howdy=yo'
last_response.body.should == 'yo'
end
Option 1
The recommended way is to use Grape::Endpoint.before_each:
context 'when user is logged in' do
before do
Grape::Endpoint.before_each do |endpoint|
allow(endpoint).to receive(:current_user).and_return(user)
end
end
after { Grape::Endpoint.before_each nil }
end
But this is quite verbose. It can live in a shared context, but you can't pass user as a parameter explicitly so you'd end up with:
let(:user) { create(:user) }
# ...
include_context 'signed in user'
Option 2
My preferred way is a more RSpec-like stubbing:
# helper
module AuthHelper
def current_user
# ...
end
end
# api
module API
module V1
class Auth < Grape::API
helpers AuthHelper
end
end
end
# spec
before do
allow_any_instance_of(AuthHelper).to receive(:current_user).and_return(user)
end
Option 3
You can also define helpers:
API::V1::User.helpers do
def current_user
user
end
end

Accessing Methods in Ruby Module

I'm writing my first Ruby module and I have this:
/app/module/test_modules/test.rb
test.rb looks similar to:
module TestModules
module Test
def test
puts 'this is a test'
end
end
end
When I call the following from console, I get:
(main)> TestModule::Test.test
//NoMethodError: private method `test' called for TestModules::Test:Module
How do I make test() visible?
You are calling a class method, whereas you defined test as an instance method. You could call it the way you want if you used the module via include or extend. This article does a good job explaining.
module TestModules
module Test
def self.test
puts 'this is a test'
end
end
end
Also,
1)
module TestModules
module Test
def test
puts 'this is a test'
end
module_function :test
end
end
2)
module TestModules
module Test
extend self
def test
puts 'this is a test'
end
end
end
The way that you have defined your method, it is a method on an instance of Test - thus it would work if you did:
blah = TestModule::Test.new
blah.test
note - and do use it this way, you would need to define Test as a class not a module
If you want the function to work on the class itself, then you need to define it like so:
def self.test
....
end
And then you can do TestModules::Test.test
the test method you defined is instance method...try this
module TestModules
module Test
def self.test
puts 'this is a test'
end
end
end
now you can call the method by this TestModules::Test.test

rails moving layout helpers in it's own module separate from application

My goal is to keep in application_helper only the methods that I use in the templates, otherwise my application_helper gets too long.
My problem relies in the suffix Helper:
This works
# app/helpers
module ApplicationHelper
include ApplicationLayout
end
# app/helpers/layouts
module ApplicationLayout
def my_helper
puts 'my_helper!'
end
end
This doesn't
# app/helpers
module ApplicationHelper
include ApplicationLayoutHelper
end
# app/helpers/layouts
module ApplicationLayoutHelper
def my_helper
puts 'my_helper!'
end
end
The error is:
Expected app/helpers/layouts/application_layout_helper.rb to define Layouts::ApplicationLayoutHelper
So I nest it and I get:
Routing Error undefined method `sub' for nil:NilClass
Actually I'd like to implement (give your opinion) app/helpers/layouts/application_helper.rb but it gives the same error, so for simplicity I stated this case.
Any suggestion on how to extract helper methods for layouts? Is there any convention over configuration I could use?
Thank you
Well, I tried what you expect and it's pretty straight:
in helpers/layouts/application_layout_helper.rb
module Layouts
module ApplicationLayoutHelper
def my_helper
'my_helper!'
end
end
end
in helpers/application_helper.rb
module ApplicationHelper
include Layouts::ApplicationLayoutHelper
end
Ad in my view I directly do:
<%= my_helper %>
I still don't really understand what you'll do with these methods.

Resources