Trying to learn Ruby and below is a module I have created in order to test IO Ruby features. When I run the following tests:
subject{TestGem.new}
it 'should be_a Module' do
subject.is_a Module
end
it 'creates a config file' do
subject.init_config
File.open("program.config", "r") { |io| io.readline }.should eq "default_file.txt"
end
I get this error for both:
Failure/Error: subject{TestGem.new}
NoMethodError:
undefined method `new' for TestGem:Module
Here is the Module I am testing. Any help/suggestions would greatly appreciated :)
$LOAD_PATH.unshift File.expand_path("../test_gem", __FILE__)
require 'version'
require 'hello'
module TestGem
#default_file_name
#supported_types
def initialize
default_file_name = 'default_file.txt'
supported_types = ['txt', 'pdf']
end
puts "module TestGem defined"
def self.init_config
File.open('program.config', "w") do |f|
f.write(yaml(#default_file_name))
f.write(yaml(#supported_types))
end
end
class MyFile
def self.first(filename)
File.open(filename, "r") {|f| f.readline}
end
def self.last(filename)
File.open(filename, "r")[-1]
end
end
class MyError < StandardError
puts "Standard Error"
end
end
Short answer: You can't instantiate modules objects
module A
end
class B
end
B.methods - A.methods #=> [:new, :superclass, :allocate]
To test a module you can include it in a object like this
object = Object.new
object.extend(TestGem)
Or you can create some example class if your module depends on some class behavior.
Related
I've been trying to stub a private module method for the whole day now but with not progress.
Here is a snippet of my application controller class
class ApplicationController < ActionController::Base
include Cesid::Application
end
Cesid > Application.rb
module Cesid
module Application
extend ActiveSupport::Concern
included do
before_action :track_marketing_suite_cesid, only: [:new]
end
private
def track_marketing_suite_cesid
return unless id_token_available?
## #cesid_auth = Auth.new(#id_token)
#cesid_auth = Auth.new(id_token)
return unless #cesid_auth.present? && #cesid_auth.valid?
#cesid_admin = Admin.where(email: #cesid_auth.email).first_or_initialize
end
def id_token_available?
## #id_token.present?
id_token.present?
end
def id_token
#id_token ||= id_token_param
end
def id_token_param
cookies[:id_token]
end
end
end
Now, I'm trying to create a simple unit test for the method
id_token_available?
And I am just trying to set the id_token_param to a random value.
I've tried using this code as stated Is there a way to stub a method of an included module with Rspec?
allow_any_instance_of(Cesid).to receive(:id_token_param).and_return('hello')
but I just get this error
NoMethodError:
undefined method `allow_any_instance_of' for #<RSpec::ExampleGroups::CesidApplication::CesidAuthorizations::GetCesidApplication:0x00007fa3d200c1c0> Did you mean? allow_mass_assignment_of
Rspec file
require 'rails_helper'
describe Cesid::Application, :type => :controller do
describe 'cesid application' do
before do
allow_any_instance_of(ApplicationController).to receive(:id_token_param).and_return('hello')
end
it 'returns true if the id_token is present' do
expect(Cesid::Application.send('id_token_available?')).to eql(true)
end
end
end
Rspec version
3.5.4
This is honestly starting to drive me crazy
I see three issues:
You call allow_any_instance_of in a context in which it is not defined. allow_any_instance_of can be used in before blocks. I need to see your RSpec code to be more specific.
Actually your code is called on the ApplicationController, not on the module, therefore you need to change your stub to
allow_any_instance_of(ApplicationController).to receive(:id_token_param).and_return('hello')
Currently id_token_param will not be called at all, because id_token_available? checks the instance variable and not the return value of the id_token method that calls the id_token_param. Just change the id_token_available? to:
def id_token_available?
id_token.present?
end
There's a much better way of going about this test. The type: :controller metadata on your spec gives you an anonymous controller instance to work with.
Here's an example of how you could write this to actually test that the before_action from your module is used:
describe Cesid::Application, type: :controller do
controller(ApplicationController) do
def new
render plain: 'Hello'
end
end
describe 'cesid before_action' do
before(:each) do
routes.draw { get 'new' => 'anonymous#new' }
cookies[:id_token] = id_token
allow(Auth).to receive(:new).with(id_token)
.and_return(instance_double(Auth, valid?: false))
get :new
end
context 'when id token is available' do
let(:id_token) { 'hello' }
it 'sets #cesid_auth' do
expect(assigns(:cesid_auth)).to be_present
end
end
context 'when id token is unavailable' do
let(:id_token) { '' }
it 'does not set #cesid_auth' do
expect(assigns(:cesid_auth)).to be_nil
end
end
end
end
Okay,
this is driving me nuts, since I don't understand the error in this case.
I have the following class defined:
module Admins
class BasePresenter < ::BasePresenter
def render_customer(id:)
return I18n.t('admin.admin') if id.nil?
::Customer.where(id: id).first.try(:name) || I18n.t('admin.deleted')
end
def percent_of(count, total)
((count.to_f / total.to_f) * 100.0).to_i
end
end
end
Which inherits from the BasePresenter below:
class BasePresenter
def initialize(object, template)
#object = object
#template = template
end
def self.presents(name)
define_method(name) do
#object
end
end
def underscored_class
#object.class.name.underscore
end
protected
def h
#template
end
def handle_none(value, html = true)
if value.present?
if block_given?
yield
else
value
end
else
return h.content_tag(:span, '-', class: 'none') if html
'-'
end
end
def current_customer
#current_customer ||= h.current_customer
end
def current_user
#current_user ||= h.current_user
end
end
However when I try to run my specs, I receive the following error from RSpec:
ArgumentError: wrong number of arguments (0 for 2)
./app/presenters/base_presenter.rb:3:in initialize'
./spec/presenters/admins/base_presenter_spec.rb:24:inblock (3
levels) in '
The class is no different from other presents, where the the inheritance works in the exact same way and those tests are passing.
Just the test for this class is failing with this error, and only when testing the method percent_of.
What am I failing to see?
EDIT
This is my RSpec test:
require 'spec_helper'
describe ::Admins::BasePresenter do
describe '#render_customer' do
let(:customer) { Customer.first }
subject { ::Admins::BasePresenter.new(Object.new, ApplicationController.new.view_context) }
it 'returns the I18n translations for (admin) when no customer is set.' do
expect(subject.render_customer(id: nil)).to eql(I18n.t('admin.admin'))
end
it 'returns the proper name when a valid ID is given' do
expect(subject.render_customer(id: customer.id)).to eql(customer.name)
end
it 'returns the I18n translations for (deleted) when an invalid ID is given' do
expect(subject.render_customer(id: -1)).to eql(I18n.t('admin.deleted'))
end
end
describe '#percent_of' do
it 'calculates the percentage correctly' do
expect(subject.percent_of(0, 1)).to eql(0)
expect(subject.percent_of(1, 1)).to eql(100)
expect(subject.percent_of(1, 2)).to eql(50)
expect(subject.percent_of(1, 3)).to eql(33)
end
end
end
Ugh,
I'm an idiot....
The problem was that my subject was defined inside a Describe block for specific tests and the second one did not have any.
Which means our hooks try to create an instance of the class in the outer describe block...
This was the fix:
require 'spec_helper'
describe ::Admins::BasePresenter do
let(:customer) { Customer.first }
subject { ::Admins::BasePresenter.new(Object.new, ApplicationController.new.view_context) }
describe '#render_customer' do
it 'returns the I18n translations for (admin) when no customer is set.' do
expect(subject.render_customer(id: nil)).to eql(I18n.t('admin.admin'))
end
it 'returns the proper name when a valid ID is given' do
expect(subject.render_customer(id: customer.id)).to eql(customer.name)
end
it 'returns the I18n translations for (deleted) when an invalid ID is given' do
expect(subject.render_customer(id: -1)).to eql(I18n.t('admin.deleted'))
end
end
describe '#percent_of' do
it 'calculates the percentage correctly' do
expect(subject.percent_of(0, 1)).to eql(0)
expect(subject.percent_of(1, 1)).to eql(100)
expect(subject.percent_of(1, 2)).to eql(50)
expect(subject.percent_of(1, 3)).to eql(33)
end
end
end
I am trying to move a helper method from a controller test to the test_helper.rb:
# example_controller_test.rb
require 'test_helper'
class ExampleControllerTest < ActionController::TestCase
should 'get index' do
turn_off_authorization
get :show
assert_response :success
end
end
# test_helper.rb
class ActionController::TestCase
def turn_off_authorization
ApplicationController.any_instance
.expects(:authorize_action!)
.returns(true)
end
end
However, I'm getting an error:
NameError: undefined local variable or method `turn_off_authorization' for #<ExampleControllerTest:0x000000067d6080>
What am I doing wrong?
Turns out that I had to wrap the helper method into a module:
# test_helper.rb
class ActionController::TestCase
module CheatWithAuth do
def turn_off_authorization
# some code goes here
end
end
include CheatWithAuth
end
I still don't know why the original version didn't work.
The idea came from another answer:
How do I write a helper for an integration test in Rails?
Edit: Another solution just came from my friend:
# test_helper.rb
class ActiveSupport::TestCase
def turn_off_authorization
# some code goes here
end
end
Note that ActiveSupport::TestCase (not ActionController::TestCase) is being used here.
My test fails when trying to find the test_helper file.
My file:
require File.dirname(__FILE__) + '/../test_helper'
require 'admin/supplier_controller'
#Re-raise errors caugth by the controller
class Admin::SupplierController; def rescue_action(e) raise e end; end
class Admin::SupplierControllerTest < ActionController::TestCase
fixtures :suppliers
def setup
#controller = Admin::SupplierController.new
#request = ActionController::TestRequest.new
#response = ActionController::TestResponse.new
end
def test_new
get :new
assert_template 'admin/supplier/new'
assert_tag 'h1', :content => 'Create new supplier'
assert_tag 'form', :attributes => {:action => '/admin/supplier/create'}
end
end
No need to do this:
require File.dirname(__FILE__) + '/../test_helper'
Just require 'test_helper' on the top of the test file, and add -Itest option to run the test. (assuming test_helper.rb is located in test/ directory)
How can I stub a method within a module:
module SomeModule
def method_one
# do stuff
something = method_two(some_arg)
# so more stuff
end
def method_two(arg)
# do stuff
end
end
I can test method_two in isolation fine.
I would like to test method_one in isolation too by stubbing the return value of method_two:
shared_examples_for SomeModule do
it 'does something exciting' do
# neither of the below work
# SomeModule.should_receive(:method_two).and_return('MANUAL')
# SomeModule.stub(:method_two).and_return('MANUAL')
# expect(described_class.new.method_one).to eq(some_value)
end
end
describe SomeController do
include_examples SomeModule
end
The specs in SomeModule that are included in SomeController fail because method_two throws an exception (it tries to do a db lookup that has not been seeded).
How can I stub method_two when it is called within method_one?
allow_any_instance_of(M).to receive(:foo).and_return(:bar)
Is there a way to stub a method of an included module with Rspec?
This approach works for me
shared_examples_for SomeModule do
let(:instance) { described_class.new }
it 'does something exciting' do
instance.should_receive(:method_two).and_return('MANUAL')
expect(instance.method_one).to eq(some_value)
end
end
describe SomeController do
include_examples SomeModule
end