Rspec test helper - ruby-on-rails

I am having a hard time figuring out how to test this helper because current_group is not defined in my test case and I am not sure how I can stub it.
module Admin
module EmployeesHelper
def upload_access
policy(current_group).can_bulk_create_employees?
end
def dashboard_params
download_employee_url = upload_access ?
download_admin_group_employees_import_csv_index_path(current_group) : nil
upload_employee_url = upload_access ?
admin_group_employees_import_csv_index_path(current_group) : nil
make_hash(upload_employee_url, download_employee_url)
end
private
def make_hash(upload_url, download_url)
{
employees: #employees,
addEmployeeUrl: new_admin_group_employee_path(current_group),
terminated_employees: #terminated_employees,
new_employees: #new_employees,
test_employees: #test_employees,
group_id: current_group.id,
downloadEmployeeUrl: download_url,
uploadEmployeeUrl: upload_url
}
end
end
end
Here's what my test looks like, but it fails because current_group is not defined.
require 'rails_helper'
describe Admin::EmployeesHelper do
let!(:group) { create(:group) }
before do
# This stub doesn't work because helper doesn't implement current_group
allow(helper).to receive(:current_group).and_return(group)
end
it 'returns correct dashboard props' do
allow(helper).to receive(:upload_access).and_return(true)
props = helper.dashboard_params
expect(props).values_at(:downloadEmployeeUrl, :uploadEmployeeUrl).should_not include(nil)
end
end

If the problem is only with current_group method, you could include this helper in a dummy class. Something like this:
let(:klass) {
Class.new do
include Admin::EmployeesHelper
def current_group
nil
end
end
}
let(:instance) { klass.new }
before do
allow(instance).to receive(:current_group).and_return('whatever') # test-specific group
end
it 'returns correct dashboard props' do
allow(instance).to receive(:upload_access).and_return(true)
props = instance.dashboard_params
expect(props).values_at(:downloadEmployeeUrl, :uploadEmployeeUrl).should_not include(nil)
end
Although I foresee that you'll have to include something for the url helpers too. And set up those instance vars. All in all, it probably isn't worth the trouble.

I ended up refactoring the helper to have current_group as an argument instead.

Related

Target mailto: links with Rails::Html::TargetScrubber

I'm trying to scrub out mailto links while allowing others using Rails::HTML Scrubbers.
See: https://github.com/rails/rails-html-sanitizer
I have a Loofah fragment like this:
Loofah.fragment('tis but a scratch')
And my Scrubber is like so:
class TargetedHtmlScrubber < Rails::Html::TargetScrubber
def initialize
super
self.tags = %w[a]
end
def allowed_node?(node)
...
end
def scrub_attribute?(name)
...
end
end
But when I run scrub! in my specs, I can't see any of the methods allowed_node? or scrub_attribute being called as per the documentation.
My spec is like this. It scrubs everything..,.
require 'rails_helper'
RSpec.describe TargetedHtmlScrubber do
describe 'targeting html tags' do
it 'ignores <a>' do
fragment = html_fragment('Greg! the stop sign!!')
expect(
fragment.scrub!(subject).to_s
).to eq 'Greg! the stop sign!!'
end
it 'targets <a href=mailto:>' do
fragment = html_fragment('tis but a scratch')
expect(
fragment.scrub!(subject).to_s
).to eq 'tis but a scratch'
end
end
end
I am expecting to implement something in one of the two methods in my class.
Ok, I worked it out. To call those methods, you need to set the tags and attributes arrays:
def initialize
super
self.tags = %w[a]
self.attributes = %w[href]
end
It will then call allowed_node?(node) and scrub_attribute?(name). So in this case, I only need the tags.
To scrub out mailto: links:
class TargetedHtmlScrubber < Rails::Html::TargetScrubber
def initialize
super
self.tags = %w[a]
end
def allowed_node?(node)
href(node) !~ /^mailto:/
end
private
def href(node)
node.try(:attributes)['href'].try(:value)
end
end

Rspec: how to test Service Object method "call" which is called in Controller action create?

Can somebody help me with rspec testing method call in Service Object?
class UserEntitiesController < ApplicationController
def create
#result = UserEntities::Create.new(params).call
return render '/422.json.jbuilder', status: :unprocessable_entity unless #result
end
here is the service objects:
module UserEntities
class Create
attr_accessor :params
def initialize(params)
#params = params
end
def call
#user_entity = UserEntity.new(user_entity_params)
set_time
if #user_entity.save
#user_entity
else
error_result
end
end
private
def error_result
false
end
def user_entity_params
#params.require(:user_entity).permit(:information,
:destroy_option,
:reviews)
end
def set_time
if #params[:available_days].present?
#user_entity.termination = Time.now + #params[:available_days].days
end
end
end
end
I tried to find information how to do this, but there are not so many.
Also i read some
You can certainly write a unit test to test the Service Object standalone
In this case, create a file spec/services/user_entities/create_spec.rb
describe UserEntities::Create do
let(:params) { #values go here }
context ".call" do
it "create users" do
UserEntities::Create.new(params).call
# more test code
end
# more tests
end
end
Later in the controller tests, if you are planning to write such, you do not need to test UserEntities::Create instead you can just mock the service object to return the desired result
describe UserEntitiesController do
before do
# to mock service object in controller test
allow(UserEntities::Create).to receive(:new)
.and_return(double(:UserEntities, call: "Some Value"))
end
# controller tests go here
end
As a supplement to #bibin answer.
If you want to mock some instance's method renturn:
allow_any_instance_of(UserEntities::Create).to receive(:call).and_return("some value")
if you want to raise a eror:
allow_any_instance_of(UserEntities::Create).to receive(:call).and_raise("boom")

Testing mixin in ruby

I'm trying to write a rspec test for a mixin class. I have the following.
module one
module two
def method
method_details = super
if method_details.a && method_details.b
something
elsif method_details.b
another thing
else
last thing
end
end
end
end
Now I have mocked the "method" object that will be passed to the class.
But I'm struggling to access the super method.
I did,
let(:dummy_class) { Class.new { include one::two } }
How to pass the mocked method object to this dummy class?
How do I go about testing this? New to ruby, can someone show me a direction with this.
Thanks in advance.
UPDATE:
I tried,
let(:dummy_class) {
Class.new { |d|
include one::two
d.method = method_details
}
}
let (:method_details){
'different attributes'
}
still doesn't work. I get undefined local variable or method method_details for #<Class:0x007fc9a49cee18>
I personally test mixing with the class. Because the mixing (module) itself has no meaning unless its attached to a class/object.
Ex:
module SayName
def say_name
p 'say name'
end
end
class User
include SayName
end
So I believe you should test your module with attached to the relevant class / object.
How ever this is a different perspective on testing mixings
HTH
I think that in your specs, you'll need to explicitly provide a super class definition for when super is called in #method as "you can't mock super and you shouldn't".
I've attempted to spec out all three of your scenarios with the following minor changes:
Changed your example code slightly to become valid Ruby
Changed #method to #the_method so it doesn't conflict with Object#method
Used OpenStruct to represent the object that super returns, because all I know is that it's an object that has methods #a and #b. You can change that out as appropriate for your real specs
Copy and paste the class and specs below into a file and give them a try:
module One
module Two
def the_method
method_details = super
if method_details.a && method_details.b
'something'
elsif method_details.b
'another thing'
else
'last thing'
end
end
end
end
RSpec.describe One::Two do
require 'ostruct'
let(:one_twoable) { Class.new(super_class) { include One::Two }.new }
describe '#the_method' do
let(:the_method) { one_twoable.the_method }
context 'when method_details#a && method_details#b' do
let(:super_class) do
Class.new do
def the_method
OpenStruct.new(a: true, b: true)
end
end
end
it 'is "something"' do
expect(the_method).to eq('something')
end
end
context 'when just method#b' do
let(:super_class) do
Class.new do
def the_method
OpenStruct.new(a: false, b: true)
end
end
end
it 'is "another thing"' do
expect(the_method).to eq('another thing')
end
end
context 'when neither of the above' do
let(:super_class) do
Class.new do
def the_method
OpenStruct.new(a: false, b: false)
end
end
end
it 'is "last thing"' do
expect(the_method).to eq('last thing')
end
end
end
end

Instance variable in Rails helper not set

I try to set some options in a rails helper, but it seems it's got overridden every time.
module MetaTagHelper
def meta_options
#meta_options ||= {}
end
def add_meta_tag_options(opt)
meta_options.deep_merge(opt)
end
end
Here is the test
require 'rspec'
describe MetaTagHelper do
it 'options should be set' do
option = {region: "1"}
option2 = {country: "AT"}
helper.add_meta_tag_options(option)
helper.add_meta_tag_options(option2).should eql(option.merge option2)
end
end
expected: {:region=>"1", :country=>"AT"}
got: {:country=>"AT"}
How can I get the spec pass?
Try using deep_merge!:
def add_meta_tag_options(opt)
meta_options.deep_merge!(opt)
end

How do I make module methods available to a constant Proc variable? (Ruby)

This is in my application helper:
def call_me
"blah"
end
P = Proc.new { call_me }
def test_me
P.call
end
If I then do this in my view:
<%= test_me %>
I get an error saying that call_me is an undefined method.
How do I get the Proc to be able to call call_me? require doesn't do it. Prefixing with ApplicationHelper::call_me doesn't either. :(
This works, but I really don't like it since test_me will be called lots of times and in reality there are many many more Procs:
def test_me
p = Proc.new { call_me }
p.call
end
It should work as is in Ruby 1.9.2, but in earlier versions you can pass your helper as an argument to the Proc:
P = Proc.new { |helper| helper.call_me }
def test_me
P.call self
end
Since call_me is an instance method on your helper, you need to invoke it with an instance.
I think you were looking for this?
def call_me
Proc.new
end
proc = call_me { "blah" }
proc.call #=> "blah"
you have to pass call_me in. gets kind of convoluted...
p = Proc.new { |m| m.call }
def test_me
p.call(call_me)
end
I'm not entirely sure what your aim here is in the larger sense so this is more or less a stab in the dark...
The most pragmatic way to do this is to make the module methods static, and then simply using them wherever you want:
module Sample::SillyModule
def self.say_hello(my_cool_var)
puts "Hello #{my_cool_var}!"
end
end
proc do
Sample::SillyModule.say_hello("stackoverflow")
end

Resources