Rspec test for a helper in my case - ruby-on-rails

I am developing a Rails app. I have a helper under app/helpers/ , that's the
db_data_helper.rb.
The methods in this db_data_helper are mainly used to execute sql dump file to load data to database. And these methods in the helper are used in some Rake task.
Some sample code of the helper:
db_data_helper.rb:
module DbDataHelper
def self.load_data
# CODE TO EXECUTE SQL dump files
end
...
...
end
Now, I would like to test this helper in Rspec but I am not sure how to implement the test in Rspec for a helper like this. Could some one help me on this?

I'd simply create a class in my spec file:
class DummyDbDataHelper
extend DbDataHelper
end
describe DummyDbDataHelper do
it "tests the load_data method" do
DummyDbDataHelper.load_data.should ...
end
end

Related

Calling rspec methods from different file

I am trying to write a class in my code to wrap some of the RSpec calls. However, whenever I try to access rspec things, my class simply doesn't see the methods.
I have the following file defined in spec/support/helper.rb
require 'rspec/mocks/standalone'
module A
class Helper
def wrap_expect(dbl, func, args, ret)
expect(dbl).to receive(func).with(args).and_return(ret)
end
end
end
I get a NoMethodError: undefined method 'expect', despite requiring the correct module. Note that if I put calls to rspec functions before the module, everything is found correctly.
I've tried adding the following like to my spec_helper.rb:
config.requires << 'rspec/mocks/standalone'
But to no avail.
I managed to use class variables in my class and passing the functions through from the global context, but that solution seems quite extreme. Also I was able to pass in the test context itself and storing it, but I'd rather not have to do that either.
expect functions by default is associated with only rspec-core methods like it before . If you need to have expect inside a method, you can try adding the Rspec matcher class in the helper file.
include RSpec::Matchers
that error because the self which call expect is not the current rspec context RSpec::ExampleGroups, you could check by log the self
module A
class Helper
def wrap_expect(dbl, func, args, ret)
puts self
expect(dbl).to receive(func).with(args).and_return(ret)
end
end
end
# test case
A::Helper.new.wrap_expect(...) # log self: A::Helper
so obviously, A::Helper does not support expect
now you have 2 options to build a helper: (1) a module or (2) a class which init with the current context of test cases:
(1)
module WrapHelper
def wrap_expect(...)
puts self # RSpec::ExampleGroups::...
expect(...).to receive(...)...
end
end
# test case
RSpec.describe StackOverFlow, type: :model do
include WrapHelper
it "...." do
wrap_expect(...) # call directly
end
end
(2)
class WrapHelper
def initialize(spec)
#spec = spec
end
def wrap_expect(...)
puts #spec # RSpec::ExampleGroups::...
#spec.expect(...).to #spec.receive(...)...
end
end
# test case
RSpec.describe StackOverFlow, type: :model do
let!(:helper) {WrapHelper.new(self)}
it "...." do
helper.wrap_expect(...)
end
end

Unit testing code that references Rails models without loading the models

I am trying to unit test a Plain Old Ruby Object that has a method which calls a class method on a Rails model. The Rails app is quite large (10s of seconds to load) so I'd prefer to avoid loading all of Rails to do my unit test which should run in under 1s.
Example:
class Foo
def bar
SomeRailsModel.quxo(3)
end
end
RSpec.describe Foo do
let(:instance) { Foo.new }
it 'calls quxo on SomeRailsModel' do
expect(SomeRailsModel).to receive(:quxo)
instance.bar
end
end
The problem here is that I need to require 'rails_helper' to load up Rails in order for app/models/some_rails_model to be available. This leads to slow unit tests due to Rails dependency.
I've tried defining the constant locally and then using regular spec_helper which kind of works.
Example:
RSpec.describe Foo do
let(:instance) { Foo.new }
SomeRailsModel = Object.new unless Kernel.const_defined?(:SomeRailsModel)
it 'calls quxo on SomeRailsModel' do
expect(SomeRailsModel).to receive(:quxo)
instance.bar
end
end
This code lets me avoid loading all of Rails and executes very fast. Unfortunately, by default (and I like this) RSpec treats the constant as a partial double and complains that my SomeRailsModel constant doesn't respond to the quxo message. Verifying doubles are nice and I'd like to keep that safety harness. I can individually disable the verification by wrapping it in a special block defined by RSpec.
Finally, the question. What is the recommended way to have fast unit tests on POROs that use Rails models without requiring all of Rails while also keeping verifying doubles functionality enabled? Is there a way to create a "slim" rails_helper that can just load app/models and the minimal subset of ActiveRecord to make the verification work?
After noodling a few ideas with colleagues, here is the concensus solution:
class Foo
def bar
SomeRailsModel.quxo(3)
end
end
require 'spec_helper' # all we need!
RSpec.describe Foo do
let(:instance) { Foo.new }
let(:stubbed_model) do
unless Kernel.const_defined?("::SomeRailsModel")
Class.new { def self.quxo(*); end }
else
SomeRailsModel
end
end
before { stub_const("SomeRailsModel", stubbed_model) }
it 'calls quxo on SomeRailsModel' do
expect(stubbed_model).to receive(:quxo)
instance.bar
end
end
When run locally, we'll check to see if the model class has already been defined. If it has, use it since we've already paid the price to load that file. If it isn't, then create an anonymous class that implements the interface under test. Use stub_const to stub in either the anonymous class or the real deal.
For local tests, this will be very fast. For tests run on a CI server, we'll detect that the model was already loaded and preferentially use it. We get automatic double method verification too in all cases.
If the real Rails model interface changes but the anonymous class falls behind, a CI run will catch it (or an integration test will catch it).
UPDATE:
We will probably DRY this up a bit with a helper method in spec_helper.rb. Such as:
def model_const_stub(name, &blk)
klass = unless Kernel.const_defined?('::' + name.to_s)
Class.new(&blk)
else
Kernel.const_get(name.to_s)
end
stub_const(name.to_s, klass)
klass
end
# DRYer!
let(:model) do
model_const_stub('SomeRailsModel') do
def self.quxo(*); end
end
end
Probably not the final version but this gives a flavor of our direction.

RSpec: how can I mixin a module into RSpec test?

In my app when user share something he's rating grows. When he tries to share something twice – he will get no additional rating for second try. For application, share callback is triggered by client-side with JS, so, it's just a regular GET-request. So, I need to test this functionality. It's easy. But I'v got several sections with this behavior. Every controller from that sections have method named "rating_from_share", so tests are pretty similar. I think it is good idea to extract that test's in a mixing and include them where it should be, but I can't figure out, how can I do this.
So, is it real to include a mixing with RSpec to a RSpec test? Maybe something kind of metaprogramming can solve this problem?
P.S. realization of "rating_from_share" method is not really the same but only the output result, so I can't to aggregate it to a superclass and test them here.
EDIT:
According to Vimsha answer, should I do something like this?
Module Share
def share
it 'should be fun'
expect(#fun.isFun?).toBe == 'yup' # the #fun is declared in ShareTest
end
end
end
describe "Share Test" do
extend Share
before :each do
#fun = Fun.new
end
it 'should do test' do
share # call method from Share module, which has real RSpec code?
end
end
The code is written just here, I'm just trying to get the idea.
A common practice in RSpec is to store such logic under spec/support. For instance:
# spec/support/ratings_macros.rb
module RatingsMacros
...
end
You then need to load it from your spec_helper:
# spec/spec_helper.rb
...
RSpec.configure do |config|
...
config.include RatingsMacros
You can now call in your tests all the methods defined in the RatingsMacros module.
You can use shared examples.
These are typically saved under spec/support and loaded via spec_helper.rb. Be sure to read the docs to understand how to load the shared code--it is not automagically performed for you.
Once they are defined you can include them like so:
# spec/support/decorated_model.rb
shared_examples "decorated_model" do
it "can be decorated" do
subject.should respond_to?(:decorate)
end
end
# my_class_spec.rb
describe MyClass do
it_behaves_like "decorated_model"
end
module Share
def share
end
end
describe "Share Test" do
extend Share
end
You can call the methods of the module directly within the tests
The other answers pollute the test with the module's methods, or involve writing a dummy class. This solution uses the built-in double object as a throwaway object to extend with the module's methods.
RSpec.describe Share do
describe '#share' do
subject { double.extend(described_class) }
end
it 'does something cool' do
expect(subject.share).to eq 'something_cool'
end
end

Testing a PORO in a Rails app

I use Rspec to test my Rails app. In my Model directory I have a Ruby file called location_services.rb. In this file is
module LocationServices
class IpLocator
attr_reader :response, :status
def initialize(response, status)
....
end
end
How can I test the creation of an IpLocator object all by itself? I just want to be able to call IpLocator.create_type_1.response and test what I get whithout the whole rails stack.
create_type_1 is a class method on IpLocator that will call new to instansiate an object.
I'm assuming your file looks more like this:
module LocationServices
class IpLocator
attr_reader :response, :status
def initialize(response, status)
....
end
def self.create_type_1
self.new
# Possibly some more code here
end
end
end
You could create spec/models/location_services_spec.rb and structure it like this:
require 'spec_helper'
describe LocationServices::IpLocator do
describe '.create_type_1' do
locator = LocationServices::IpLocator.create_type_1
expect(locator).to # finish your assertion here
end
end
The naming conventions might not work quite right. If RSpec doesn't find the class it needs, you could try moving and renaming location_services.rb to app/models/location_services/ip_locator.rb. Move and rename the spec to spec/models/location_services/ip_locator_spec.rb if you do that.
Requiring the spec_helper.rb file might load the Rails stack for your tests, though. That might depend on how your file is set up.

How to test a Rails helper that depends on another Rails helper?

I am writing an RSpec spec to test a Rails helper. The problem is that the helper method that I'm testing depends on a method defined in a different helper. It may be a code smell to me, but I'm adding tests for legacy code and am not at a point where I can refactor. How can I test this Rails helper?
module FancyHelper
def fancy_method
html = "hello"
html << another_fancy_method
html
end
end
module OtherHelper
def another_fancy_method
"world"
end
end
require "spec_helper"
describe FancyHelper do
describe "#fancy_method" do
it "does what it's supposed to" do
helper.fancy_method.should match("hello")
# NoMethodError: undefined method `another_fancy_method'
end
end
end
This is what stubs are for. When testing your helper that depends on the other helper, you will want to stub the other helper to get a predictable value and complete the test.
EDIT: https://www.relishapp.com/rspec/rspec-mocks/docs/method-stubs thanks grantovich for the newer link.

Resources