How to test helper with params - ruby

I tried to test a method in helper with params[:a]. I'm not using Rspec so don't know how to solve it.
In helper file:
def get_commands(filter)
order_by = params[:order_by]
something else(filter)
end
and the test file:
test 'get_commands works' do
filter = something
res = get_commands(filter)
end
It shows: NameError: undefined local variable or method `params'
and it also doesn't work if I just add
params[:order_by]='desc'

A pretty generic approach is to use dependency injection.
First, change the method to accept params as an argument:
def get_commands(filter, params)
order_by = params[:order_by]
something else(filter)
end
Make sure to include this new argument in the controller when you call the method.
Then you can pass a mock parameter set in your test:
test 'get_commands works' do
filter = something
mock_params = ActionController::Parameters.new(order_by: :id)
res = get_commands(filter, mock_params)
# ... make your expectation about the result.
end
As a caveat, dependency injection is sometimes treated like an antipattern. Rails does have some built in helpers for testing controllers, see https://guides.rubyonrails.org/testing.html#functional-tests-for-your-controllers. But using dependency injection like this would definitely work, and is a bit simpler.

Related

How to write rspec with mocking

I want to write an rspec test by mocking this method. Should I break this method up, as it doing multiple things?
require 'yaml'
require_relative 'checkerror'
class Operations
    def initialize
        #check
  end
    def result (result_log: File.new('result.txt', 'a+'))
      if #check.errors.empty?
 result_log.write("#{#check.checker.file_path} :: No offensenses detected\n")
#checker is instance of CheckError class
  puts "#{#check.checker.file_path} :: No offensenses detected\n"
      else
        #check.errors.uniq.each do |err|  puts "#{#check.checker.file_path} : #{err}\n"
 result_log.write("#{#check.checker.file_path} : #{err}\n")
 end
end
result_log.close
end
  end
end
If #check.errors need to be stuubed with a value and check the execution block.
It's going to be awkward mocking the f object in your current implementation, due to this line:
f = File.new('result.txt', 'a+')
You'd need to write something weird in the rspec test, like:
allow(File).to receive(:new).with('result.txt', 'a+').and_return(mock_file)
So instead, I'd recommend using dependency injection to pass the file into the method. For example:
def check_result(results_log: File.new('result.txt', 'a+'))
if #errors.empty?
# ...
end
Now, your rspec test can look something like this:
let(:results_log) { Tempfile.new }
it "prints errors to log file" do
wharever_this_object_is_called.check_result(result_log: results_log)
expect(result_log.read).to eq("checker_file_path.txt :: No offences detected\n")
end

Sinatra Multiple Parallel Requests Variable Behaviour

I am fairly new to ruby and would like to understand how class instance variables behave in case of multiple parallel requests.
I have a method inside my controller class which is called everytime for each request for a specific operation (create in this case)
class DeployProvision
def self.create(data)
raise "Input JSON not received." unless data
# $logger.info input_data.inspect
failure = false
response_result = ""
response_status = "200"
#validator = SchemaValidate.new
validation = #validator.validate_create_workflow(data.to_json)
end
end
This method is called as (DeployProvision.create(data))
I am a little confused on how #validator class instance variable behaves when multiple requests come. Is it shared among multiple requests. Is it a good idea to declare this as class instance variable instead of a local variable ?
I am working on an existing code base and would like to understand the intent of creating #validator as a class instance variable instead of local variable.
You can write ultra-simple script like this:
require 'sinatra'
class Foo
def self.bar
#test = Time.now
puts #test
end
end
get '/' do
Foo.bar
end
and you'll see it does nothing, because with every call, you're creating new instance of Time(SchemaValidate in your code).
If you used memoization and had something like #validator ||= SchemaValidate.new you would have one instance of SchemaValidate stored between requests.
I don't think that'd change anything in terms of performance and I don't have idea why would anyone do something like that.
You can have some fun with ultra-simple scripts with sinatra to test how it behaves.
Good luck with this code!

rspec should received x.times a message [duplicate]

I have something like:
value = nil
if some_condition
value =my_object.do_stuff()
end
And in my test, I have the follwing:
MyObject.any_instance.should_receive(:do_stuff)
However, I'd like to just test that the method was called, and have it execute the original code. I'd like to NOT have to do it like:
MyObject.any_instance.should_receive(:do_stuff).and_return(:some_dummy_value)
Is there a way of doing that?
There is and_call_original method:
MyObject.any_instance.should_receive(:do_stuff).and_call_original
See https://github.com/rspec/rspec-mocks#delegating-to-the-original-implementation
I believe, that it's better to create object by FactoryGirl and than to test it. You can read how to make factories and so on.
Example:
FactoryGirl.define do
factory :my_object do
[*here is your attributes*]
end
end
So, after you created a factory, you should to create test where this method used and write this:
my_object = FactoryGirl.create(:my_object)
my_object.should_receive(:do_stuff)
Inside your code you will do that "do_stuff" with your "my_object" when u will run test.

Rspec -- mocking a method in another class

I'm new to RSpec, so bear with me!
Here is my code.
Testfile:
before(:all) do
#package = Package.new("testing")
#param_source = "cat #{root}/file/test.json"
end
"it should update the version params appropriately" do
group_params = mock("params")
group_params.expects(:each).multiple_yields([["staging", #param_source]])
#package.update_version(group_params)
# Some assertions here
end
Class file:
class Package
def initialize(db_file)
# Some http options set for httparty
end
def update_version(group_params)
group_params.each do |environment, param_source|
group_json = JSON.parse(HTTParty.get(param_source, #http_cred))
# Bunch more stuff done here
end
Basically, I have this test.json that I want to use to verify that things are getting parsed correctly. This HTTParty.get call expects a GET call to be made though. Is there a way I can mock that with the param_source variable. Please let me know if I need to provide more information here. Thanks for the help!
Stub:
HTTParty.stub(get: file_content)
or mock:
HTTParty.should_receive(:get).with(foo, bar).and_return(file_content)
or stubbing in the new rspec syntax:
allow(HTTParty).to receive(:get).and_return(file_content)
or mocking in the new syntax:
expect(HTTParty).to receive(:get).with(foo, bar).and_return(file_content)

Ruby: Include a dynamic module name

I have a situation in my Rails application where I need to include arbitrary modules depending on the current runtime state. The module provides custom application code that is only needed when certain conditions are true. Basically, I'm pulling the name of a company from the current context and using that as the filename for the module and its definition:
p = self.user.company.subdomain + ".rb"
if File.exists?(Rails.root + "lib/" + p)
include self.class.const_get(self.user.company.subdomain.capitalize.to_sym)
self.custom_add_url
end
My test module looks like this:
module Companyx
def custom_add_url
puts "Calling custom_add_url"
end
end
Now in the console, this actually works fine. I can pull a user and include the module like so:
[1] pry(main)> c = Card.find_by_personal_url("username")
[2] pry(main)> include c.class.const_get(c.user.company.subdomain.capitalize)=> Object
[3] pry(main)> c.custom_add_url
Calling custom_add_url
If I try to run the include line from my model, I get
NoMethodError: undefined method `include' for #<Card:0x007f91f9094fb0>
Can anyone suggest why the include statement would work on the console, but not in my model code?
I'm doing a similar thing. I found this answer useful:
How to convert a string to a constant in Ruby?
Turns out I was looking for the constantize method. This is the line I'm using in my code:
include "ModuleName::#{var.attr}".constantize
Edit:
So ack, I ran into various problems with actually using that line myself. Partially because I was trying to call it inside a method in a class. But since I'm only calling one method in the class (which calls/runs everything else) the final working version I have now is
"ModuleName::#{var.attr}".constantize.new.methodname
Obviously methodname is an instance method, so you could get rid of the new if yours is a class method.
Include is a method on a class.
If you want to call it inside a model, you need to execute the code in the context of its singleton class.
p = self.user.company.subdomain + ".rb"
if File.exists?(Rails.root + "lib/" + p)
myself = self
class_eval do
include self.const_get(myself.user.company.subdomain.capitalize.to_sym)
end
self.custom_add_url
EDIT:
class << self doesn't accept a block; class_eval does, hence it preserves the state of local variables. I've modified my solution to use it.

Resources