How to test class methods that rely on associations with RSpec and Sinatra? - ruby

I've written some RSpec tests that successfully create objects with :let statements. However, the test environment doesn't maintain the associations that function properly everywhere else. Below is an example of a class that would turn up a NoMethodError (undefined method `money' for nil:NilClass). Money is a column in Inventory. Any thoughts?
class Inventory < ActiveRecord::Base
belongs_to :character
def self.return_money(character)
character.inventory.money
end
end
And here's a corresponding example for a spec doc:
require 'spec_helper'
describe 'Test methods' do
let(:trader) {
Character.create(
name: "Trader",
location_id: 1)
}
let(:trader_inventory) {
Inventory.create(
character_id: trader.id,
storage_capacity: 50000,
money: 20000,
markup: 1.35)
}
it "test method" do
expect(Inventory.return_money(trader)).to eq(100)
end
end

There is no reason this shouldn't work. RSpec isn't special, it's just regular Ruby code. You can confirm this by moving all of your code into a single file, something like:
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
ActiveRecord::Base.logger = Logger.new(STDOUT)
class Inventory < ActiveRecord::Base
end
class Character < ActiveRecord::Base
has_one :inventory
end
describe 'test' do
it 'works' do
puts Character.first.inventory.money.inspect
end
end
Guesses as to what may be broken:
money is a composite field or something like that. Can you post your database schema?
Library files aren't being loaded correctly. Use puts $LOADED_FEATURES to verify that all the files that should be required have been.

Related

Searchkick not returning any results in RSpec

I'm trying to test a class with RSpec which makes use of Searchkick.
I have the following in my spec support directory:
RSpec.configure do |config|
config.before(:suite) do
# Reindex models
Restaurant.reindex
# and disable callbacks
Searchkick.disable_callbacks
end
config.around(:each, search: true) do |example|
Searchkick.callbacks(true) do
example.run
end
end
end
and my test looks like this:
describe Restaurants::SearchFacade, search: true do
subject { described_class }
describe '#search' do
context 'with a query' do
let!(:restaurant_1) { create(:restaurant, :reindex, description: 'Amazing sour dough pizza place') }
let!(:restaurant_2) { create(:restaurant, :reindex) }
it 'returns values containing the query' do
Restaurant.search_index.refresh
params = { search: { query: 'sour dough' } }
facade = subject.new(params)
expect(facade.search.total_count).to eq(1)
end
end
end
end
I should be receiving 1 result. However, Searchkick returns 0 results. This is not a problem with the facade since I've also tried with Restaurant.search('*') which should return all results but nothing is being returned.
My Restaurant class looks like this:
class Restaurant < ApplicationRecord
searchkick searchable: %i[title description street_address]
end
and my factory looks something like this:
FactoryBot.define do
factory :restaurant do
association :plan
association :company
sequence :title do |n|
"Restaurant #{n}"
end
description 'An awesome restaurant'
# More attributes here...
trait :reindex do
after(:create) do |restaurant, _evaluator|
restaurant.reindex(refresh: true)
end
end
end
end
Any idea what may be causing this? Any help would be much appreciated. Thanks in advance!
Try getting rid of all of that fancy stuff you are doing in your before(:suite) and your factories. You just need to be doing it before each spec (if the search tag is set).
I was able to get searchkick working with rspec and indexing my models with only this small amount of code in my spec_helper.rb:
RSpec.configure do |config|
config.before(:each) do |example|
# Elasticsearch / Searchkick
if example.metadata[:search]
Searchkick.enable_callbacks
Restaurant.reindex
else
Searchkick.disable_callbacks
end
end
end
Side note: You may still need to run the Restaurant.search_index.refresh code after creating a bunch of Restaurant records in your spec setup. I'm not entirely sure?

How to write rspec testing without database in ruby for this ruby code

I am creating a ruby project with the class which is the inheritance of ActiveRecord::Base. How can i write rspec testing and simple coverage for the following code sample without using database.
class Person < ActiveRecord::Base
validates_length_of :name, within: 10..40
end
person = Person.create(:name => "aungaung")
person.save
If you don't want to touch db, FactoryGirl.build_stubbed is your friend.
> person = FactoryGirl.build_stubbed :person
> person.save!
> #=> person obj
> Person.all
> #=> [] # Not saved in db
So, to test validation
it "validates name at length" do
person = FactoryGirl.build_stubbed :person, name: "aungaung"
expect{person.save!}.to raise_error(ActiveRecord::RecordInvalid)
end
Note build_stubbed is good at model's unit testing. For anything UI related, you can't use this method and need to save to db actually.
Here's a short example of testing the validations on an ActiveRecord model. You can certainly go into much more depth, and there are plenty of ways to make the tests more elegant, but this will suffice for a first test.
describe Person do
describe "#name" do
specify { Person.new(:name => "Short").should_not be_valid }
specify { Person.new(:name => "Long" * 12).should_not be_valid }
specify { Person.new(:name => "Just Right").should be_valid }
end
end

Testing the "accepts_nested_attributes_for" in unit testing using Rspec

I am new to rails and testing models. My model class is like this:
class Tester < Person
has_one :company
accepts_nested_attributes_for :skill
end
And I want to do test for "accepts_nested_attributes_for :skill" using rspec with out any other gem. How can I accomplish this?
There are convenient shoulda gem matchers for testing accepts_nested_attributes_for, but you mentioned that you don't want to use other gems. So, using Rspec only, the idea is to set attributes hash that would include required Tester attributes and nested hash called skill_attributes that would include required Skill attributes; then pass it into create method of Tester, and see if it changes the number of Testers and number of Skills. Something like that:
class Tester < Person
has_one :company
accepts_nested_attributes_for :skill
# lets say tester only has name required;
# don't forget to add :skill to attr_accessible
attr_accessible :name, :skill
.......................
end
Your tests:
# spec/models/tester_spec.rb
......
describe "creating Tester with valid attributes and nested Skill attributes" do
before(:each) do
# let's say skill has languages and experience attributes required
# you can also get attributes differently, e.g. factory
#attrs = {name: "Tester Testov", skill_attributes: {languages: "Ruby, Python", experience: "3 years"}}
end
it "should change the number of Testers by 1" do
lambda do
Tester.create(#attrs)
end.should change(Tester, :count).by(1)
end
it "should change the number of Skills by 1" do
lambda do
Tester.create(#attrs)
end.should change(Skills, :count).by(1)
end
end
Hash syntax may be different. Also, if you have any uniqueness validations, make sure you are generating #attrs hash dynamically before every test.
Cheers, mate.

Rails3: Nested model - child validates_with method results in "NameError - uninitialized constant [parent]::[child]"

Consider the following parent/child relationship where Parent is 1..n with Kids (only the relevant stuff here)...
class Parent < ActiveRecord::Base
# !EDIT! - was missing this require originally -- was the root cause!
require "Kid"
has_many :kids, :dependent => :destroy, :validate => true
accepts_nested_attributes_for :kids
validates_associated :kids
end
class Kid < ActiveRecord::Base
belongs_to :parent
# for simplicity, assume a single field: #item
validates_presence_of :item, :message => "is expected"
end
The validates_presence_of methods on the Kid model works as expected on validation failure, generating a final string of Item is expected per the custom message attribute supplied.
But if try validates_with, instead...
class Kid < ActiveRecord::Base
belongs_to :parent
validates_with TrivialValidator
end
class TrivialValidator
def validate
if record.item != "good"
record.errors[:base] << "Bad item!"
end
end
end
...Rails returns a NameError - uninitialized constant Parent::Kid error following not only an attempt to create (initial persist) user data, but also when even attempting to build the initial form. Relevant bits from the controller:
def new
#parent = Parent.new
#parent.kids.new # NameError, validates_* methods called within
end
def create
#parent = Parent.new(params[:parent])
#parent.save # NameError, validates_* methods called within
end
The error suggests that somewhere during model name (and perhaps field name?) resolution for error message construction, something has run afoul. But why would it happen for some validates_* methods and not others?
Anybody else hit a wall with this? Is there some ceremony needed here that I've left out in order to make this work, particularly regarding model names?
After a few hours away, and returning fresh -- Was missing require "Kid" in Parent class. Will edit.

Should the Applicant class "require 'mad_skills'" or "include 'mad_skills'"?

Also, what does "self.send attr" do? Is attr assumed to be a private instance variable of the ActiveEngineer class? Are there any other issues with this code in terms of Ruby logic?
class Applicant < ActiveEngineer
require 'ruby'
require 'mad_skills'
require 'oo_design'
require 'mysql'
validates :bachelors_degree
def qualified?
[:smart, :highly_productive, :curious, :driven, :team_player ].all? do
|attr|
self.send attr
end
end
end
class Employer
include TopTalent
has_millions :subscribers, :include=>:mostly_women
has_many :profits, :revenue
has_many :recent_press, :through=>[:today_show, :good_morning_america,
:new_york_times, :oprah_magazine]
belongs_to :south_park_sf
has_many :employees, :limit=>10
def apply(you)
unless you.build_successful_startups
raise "Not wanted"
end
unless you.enjoy_working_at_scale
raise "Don't bother"
end
end
def work
with small_team do
our_offerings.extend you
subscribers.send :thrill
[:scaling, :recommendation_engines, : ].each do |challenge|
assert intellectual_challenges.include? challenge
end
%w(analytics ui collaborative_filtering scraping).each{|task|
task.build }
end
end
end
def to_apply
include CoverLetter
include Resume
end
require 'mad_skills' loads the code in mad_skills.rb (or it loads mad_skills.so/.dll depending on which one exists). You need to require a file before being able to use classes, methods etc. defined in that file (though in rails files are automatically loaded when trying to access classes that have the same name as the file). Putting require inside a class definition, does not change its behaviour at all (i.e. putting it at the top of the file would not make a difference).
include MadSkills takes the module MadSkills and includes it into Applicant's inheritance chain, i.e. it makes all the methods in MadSkills available to instances of Applicant.
self.send attr executes the method with the name specified in attr on self and returns its return value. E.g. attr = "hello"; self.send(attr) will be the same as self.hello. In this case it executes the methods smart, highly_productive, curious, driven, and team_player and checks that all of them return true.

Resources