I am having trouble parameterizing a shared_example in my rspec controller testing. My code is based on Passing Examples to a Shared Group as outlined on Relish
On the face of it, it seems like instance variables created in a shared_context are not available in shared_examples. Any ideas?
I am using Rails 3.1, with CanCan and Devise gems.
Controller (app/controllers/stores_controller.rb):
class StoresController < SubDomainController
def index
authorize! :list, Store
#stores = current_user.company.stores.allrespond_to do |format|
format.html # index.html.erb
format.json { render json: #stores }
end
end
end
Shared Module (spec/support/shared.rb):
module Shared
shared_context "stores controller" do
before :all do
build_companies
build_stores
build_users
end
end
shared_context "admin logged in" do
before :each do
login_admin
end
end
#########THIS IS THE TEST THAT IS FAILING#########
shared_examples "authorised_user" do |values|
it "should assign correct variables" do
values.each do |key,val|
assigns[key].should == values[key]
end
end
end
end
Helpers Module (spec/support/helpers.rb):
module Helpers
def build_companies
#company = build_company(get_rand_string() ,"Correct")
#other_company = build_company(get_rand_string() ,"Invalid")
end
def build_stores
#store = FactoryGirl.create(:store, :company_id => #company.id)
#other_store = FactoryGirl.create(:store, :company_id => #company.id)
#other_company_store = FactoryGirl.create(:store, :company_id => #other_company.id)
end
def build_users
#admin_user = FactoryGirl.create(:user, :role_id => 1, :company => #company, :store_id => #store.id)
#manager_user = FactoryGirl.create(:user, :role_id=> 2, :company => #company, :store_id => #store.id)
#regular_user = FactoryGirl.create(:user, :role_id=> 3, :company => #company, :store_id => #store.id)
end
end
Spec Helpers (spec/spec_helper.rb):
.....
config.include Helpers
config.include Shared
....
Controller Spec (spec/controllers/stores_controller_spec.rb):
require 'spec_helper'
describe StoresController do
include_context "stores controller"
describe "INDEX" do
[:admin].each do |u|
describe "#{u} authorised" do
include_context "#{u} logged in"
before :each do
get :index
end
###passing instance variables to Helper module not working######
it_behaves_like "authorised_user", {:stores => [#store, #other_store]}
end
end
end
end
end
Errors:
1) StoresController INDEX company stores admin authorised behaves like authorised_user should assign correct variables
Failure/Error: assigns[key].should == values[key]
expected: [nil, nil]
got: [#<Store id: 1, company_id: 1, location_name: "MyString", address: "MyString", created_at: "2012-06-18 05:29:19", updated_at: "2012-06-18 05:29:19">, #<Store id: 2, company_id: 1, location_name: "MyString", address: "MyString", created_at: "2012-06-18 05:29:19", updated_at: "2012-06-18 05:29:19">] (using ==)
Diff:
## -1,2 +1,3 ##
-[nil, nil]
+[#<Store id: 1, company_id: 1, location_name: "MyString", address: "MyString", created_at: "2012-06-18 05:29:19", updated_at: "2012-06-18 05:29:19">,
+ #<Store id: 2, company_id: 1, location_name: "MyString", address: "MyString", created_at: "2012-06-18 05:29:19", updated_at: "2012-06-18 05:29:19">]
Figured this out myself.
The variable needs to be set using the 'let' syntax in stores_controller_spec.rb as follows
it_behaves_like "authorised_user" do
let(:variable){stores}
let(:values){[#store, #other_store]}
end
Then in the shared_examples.rb:
shared_examples "authorised_user" do
it "should assign correct variables" do
assigns[variable].should == values
end
end
Related
github_asset.rb
# frozen_string_literal: true
require 'asset_ingester/helpers/project_details'
require 'active_model'
module AssetIngester
module Asset
class GithubAsset
include ActiveModel::Serializers::JSON
attr_reader :id, :name, :full_name, :description, :owner_name, :owner_url,
:owner_avatar_url, :url, :html_url, :artifact_id, :jiras, :asset_type
# Public: Initializes an instance of the GithubAsset class
#
# repo - A hash containing github repository details
# asset_type - A string representation of the asset type
def initialize(repo, asset_type)
#id = repo[:id]
#name = repo[:name]
#full_name = repo[:full_name]
#description = repo[:description]
#owner_name = repo.dig(:owner, :login)
#owner_url = repo.dig(:owner, :url)
#owner_avatar_url = repo.dig(:owner, :avatar_url)
#url = repo[:url]
#html_url = repo[:html_url]
#asset_type = asset_type
#artifact_id = repo[:artifact_id] if repo[:artifact_id] && !repo[:artifact_id].empty?
#jiras = repo[:jiras] if repo[:jiras] && !repo[:jiras].empty?
end
# Public: Defines the JSON serialization structure
#
# https://edgeguides.rubyonrails.org/active_model_basics.html#serialization
def attributes
{
'id' => #id,
'name' => #name,
'full_name' => #full_name,
'description' => #description,
'owner_name' => #owner_name,
'owner_url' => #owner_url,
'owner_avatar_url' => #owner_avatar_url,
'url' => #url,
'html_url' => #html_url,
'asset_type' => #asset_type,
'artifact_id' => #artifact_id,
'jiras' => #jiras
}.compact
end
end
end
end
github_asset_spec.rb
require 'asset_ingester/asset/github_asset'
RSpec.describe AssetIngester::Asset::GithubAsset, type: :api do
context "creating" do
let(:asset_type) {"node_package"}
let(:repo) do
[id: 131_690,
name: 'acm-care-management-js',
full_name: 'AcuteCaseManagementUI/acm-care-management-js',
owner_name: 'AcuteCaseManagementUI',
owner_url: 'https://github.cerner.com/api/v3/users/AcuteCaseManagementUI',
owner_avatar_url: 'https://avatars.github.cerner.com/u/4095?',
url: 'https://github.cerner.com/api/v3/repos/AcuteCaseManagementUI/acm-care-management-js',
html_url: 'https://github.cerner.com/AcuteCaseManagementUI/acm-care-management-js',
asset_type: 'node_package',
artifact_id: "",
jiras: [] ]
end
describe '::attributes' do
subject { AssetIngester::Asset::GithubAsset.attributes(repo, asset_type) }
it 'instantiates the class with 2 arguments' do
expect(subject).to be_an_instance_of(AssetIngester::Asset::GithubAsset)
end
it 'sets a to the first argument' do
expect(subject.repo).to eq(repo)
end
it 'sets b to the second argument' do
expect(subject.asset_type).to eq(asset_type)
end
end
end
end
This is how i tried testing the github_asset.rb file but however I'am receiving the following error while defining the subject
AssetIngester::Asset::GithubAsset creating ::attributes instantiates the class with 2 arguments
Failure/Error: subject { AssetIngester::Asset::GithubAsset.attributes(repo, asset_type) }
NoMethodError:
undefined method `attributes' for AssetIngester::Asset::GithubAsset:Class
Did you mean? attr_writer
I am green to RSpec testing and want to know how this can be done.
You get that NoMethodError because you are trying to call attributes as a class method:
subject { AssetIngester::Asset::GithubAsset.attributes(repo, asset_type) }
# ^^^^^^^^^^
although in your code, attributes is defined as an instance method.
But apart from that, you ask "how ruby initialize method can be tested" and apparently your test code is all about initialize and not about attributes, so let's start at the beginning:
describe '::attributes' do
# ...
end
You want to test initialize, so attributes should be initialize. And since initialize is an instance method (rather than a class method), :: should be #:
describe '#initialize' do
# ...
end
And your subject should be an instance of GithubAsset which is created via new1:
describe '#initialize' do
subject { AssetIngester::Asset::GithubAsset.new(repo, asset_type) }
# ...
end
With that in place, you can start writing your tests, e.g.:
describe '#initialize' do
subject { AssetIngester::Asset::GithubAsset.new(repo, asset_type) }
it 'sets the id attribute' do
expect(subject.id).to eq(131690)
end
end
1 In Ruby, you rarely invoke ::allocate and #initialize directly. So instead of:
obj = Array.allocate
obj.send(:initialize, 5) { |i| i ** 2 }
obj #=> [0, 1, 4, 9, 16]
you typically just call new and let it call allocate and initialize for you:
obj = Array.new(5) { |i| i ** 2 }
obj #=> [0, 1, 4, 9, 16]
With this setup in minitest,
def test_id
i = Item.new({
:id => 1,
:name => "Pencil",
:description => "You can use it to write things",
:unit_price => BigDecimal.new(10.99,4),
:created_at => Time.now,
:updated_at => Time.now,
:merchant_id => 2
})
assert_equal 1, i.id
end
For some reason, when created, calling the id attribute results in an array of all of the values:
[1,'Pencil','You can use it to write things',#<BigDecimal...>, 2018-07-24 14:43:36 -0600, 2018-07-24 14:43:36 -0600, 2]
instead of the integer 1.
In the item file, it looks like what you would expect
require 'bigdecimal'
require 'time'
class Item
attr_reader :id, :created_at, :merchant_id
attr_accessor :name, :description, :unit_price, :updated_at
def initialize(item_data)
#id = item_data[:id].to_i,
#name = item_data[:name],
#description = item_data[:description],
#unit_price = BigDecimal.new(item_data[:unit_price], 4),
#created_at = item_data[:created_at],
#updated_at = item_data[:updated_at],
#merchant_id = item_data[:merchant_id].to_i
end
end
Not really sure how this is happening.
Throwing a pry in the test method before the assertion and calling i results in
#<Item:0x00007f8cc48eb4f0
#created_at=2018-07-24 15:14:55 -0600,
#description="You can use it to write things",
#id=[1, "Pencil", "You can use it to write things", #<BigDecimal:7f8cc48eb4c8,'0.1099E2',18(27)>, 2018-07-24 15:14:55 -0600, 2018-07-24 15:14:55 -0600, 2],
#merchant_id=2,
#name="Pencil",
#unit_price=#<BigDecimal:7f8cc48eb4c8,'0.1099E2',18(27)>,
#updated_at=2018-07-24 15:14:55 -0600>
in the terminal.
It's your trailing commas in the initializer:
def initialize(item_data)
#id = item_data[:id].to_i, # <=
#name = item_data[:name], # <=
What they do is make ruby see the method like this:
#id = [item_data[id].to_i, #name = item_data[:name], ...]
It seems that the problem are the commas that you are adding at the end of each setting variable. Check with this code:
require 'bigdecimal'
require 'time'
class Item
attr_reader :id, :created_at, :merchant_id
attr_accessor :name, :description, :unit_price, :updated_at
def initialize(item_data)
#id = item_data[:id].to_i
#name = item_data[:name]
#description = item_data[:description]
#unit_price = BigDecimal.new(item_data[:unit_price], 4)
#created_at = item_data[:created_at]
#updated_at = item_data[:updated_at]
#merchant_id = item_data[:merchant_id].to_i
end
end
I am learning Rails and try to use state machine but i am not able to initialize value for state variable..can anyone help me
class Primer < ActiveRecord::Base
extend StateMachine::MacroMethods
state_machine :initial => :data do
event :ignite do
transition :parked => :idling
end
end
end
database schema
class CreatePrimers < ActiveRecord::Migration
def change
create_table :primers do |t|
t.string :name
t.string :state
t.string :desc
t.timestamps null: false
end
end
end
try with
rails c
a = Primer.new
=> #<Primer id: nil, name: nil, state: nil, desc: nil, created_at: nil, updated_at: nil>
2.2.0 :002 > a.state
=> nil
Seems like a known issue https://github.com/pluginaweek/state_machine/issues/334
And new gem suggested is https://github.com/state-machines/state_machines-activerecord
The following models are linked via belongs_to:
require 'mongoid'
class Sensor
include Mongoid::Document
field :sensor_id, type: String
validates_uniqueness_of :sensor_id
end
...
require 'mongoid'
require_relative 'sensor.rb'
class SensorData
include Mongoid::Document
belongs_to :sensor
field :date, type: Date
field :ozonMax1h, type: Float
field :ozonMax8hMittel, type: Float
index({ date: 1, sensor_id: 1 }, { unique: true })
end
Here is a Sinatra app which provides a few API paths based on these models:
require 'sinatra'
require 'csv'
require_relative './models/sensor.rb'
require_relative './models/sensor_data.rb'
configure do
Mongoid.load!('./mongoid.yml')
end
def prepare_for_export(sensor_data)
converted_data = sensor_data.asc(:date).map do |e|
{
sensor_id: e.sensor.nil? ? :null : e.sensor.sensor_id,
date: e.date,
ozonMax1h: e.ozonMax1h,
ozonMax8hMittel: e.ozonMax8hMittel
}
end
converted_data
end
def convert_to_json(sensor_data)
prepare_for_export(sensor_data).to_json
end
def convert_to_csv(sensor_data)
data = prepare_for_export sensor_data
csv_string = CSV.generate do |csv|
csv << data.first.keys
data.each do |hash|
csv << hash.values
end
end
csv_string
end
def get_recent
max_date = SensorData.max(:date)
SensorData.where(date: max_date)
end
def get_for_year(year)
SensorData.where(:date.gte => Date.new(year, 1, 1)).where(:date.lte => Date.new(year, 12, 31))
end
def get_for_sensor(sensor)
foo = SensorData.where(sensor_id: sensor)
puts "hallo"
return foo
end
get '/api/v1/stations' do
content_type :json
Sensor.all.map { |e| {sensor_id: e.sensor_id} }.to_json
end
get '/api/v1/sensordata/:year' do
content_type :json
convert_to_json get_for_year(params[:year].to_i)
end
get '/api/v1/sensordata/:year/csv' do
convert_to_csv get_for_year(params[:year].to_i)
end
get '/api/v1/recent' do
content_type :json
convert_to_json get_recent
end
I would like to output the SensorData for a particular sensor such as here:
/api/v1/stations/:sensor_id/sensordata/:year/csv
I am not sure what you are trying to do or even if you are still looking for an answer but here it goes. Something seems wrong with the models in the example you have here. Sounds like part of what you are doing would work if Sensor knows about sensor_data. So might need to add this to Sensor class:
has_many :sensor_data
Though the singular of data is datum. The class would be expected to be SensorDatum. If you can't change it, you need to tell Mongoid the class_name to expect in the has_many is actuall SensorData.
You CAN specify foreign_key in Mongoid with belongs_to.
You CANNOT filter with the belongs_to like you can with ActiveRecord, but you can use scopes outside of the belongs_to to get the same effect. Exampe:
belongs_to :sensor
scope :for_year, -> (year) { where(:date.gte => Date.new(2015,1,1)).where(:date.lte => Date.new(2015, 12, 31))}
or
belongs_to :sensor
def self.for_year year
where(:date.gte => Date.new(year,1,1)).where(:date.lte => Date.new(year, 12, 31))
end
So your query would become something like this:
sensor = Sensor.find_by(sensor_id: params[:sensor_id])
sensor.sensor_data.for_year(2015)
Spec:
require 'spec_helper'
describe UsersController do
describe "GET index" do
it "assigns #users" do
user = User.create(:email => 'bob#test.com', :password=>'12', :password_confirmation=> '12')
get :index
assigns(:users).should eq([user])
end
it "renders the index template" do
get :index
response.should render_template("index")
end
end
end
Code:
class UsersController < ApplicationController
def index
#users=User.all
end
end
Error:
Failures:
1) UsersController GET index assigns #users
Failure/Error: #users.should eq([user])
expected: [#<User id: nil, email: "bob#test.com", encrypted_password: "$2a$04$JRFUhZxmw1jVCVRFx1bkIO9dpiJbVpbBhlXkGq.zbVyj...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, created_at: nil, updated_at: nil, admin: nil>]
got: []
(compared using ==)
The problem was that the password of '12' was too short.
One way to avoid that problem in the future is using factories and including factory specs so you can rest assured you're using valid objects.
Thoughtbot describes a nice approach to this using their very own FactoryGirl in this blog post. I'm not a big fan of the rake task, but the factory spec they recommend is pretty useful.