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

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

Related

What is 'valid?' in RSpec? Where can I look at it?

I've attempted to create a model, which needs to pass a series of validation tests in RSpec. However, I constantly get the error
expected #<Surveyor::Answer:0x0055db58e29260 #question=#<Double Surveyor::Question>, #value=5> to respond to `valid?`
My understanding (from here) was that 'valid?' checks that no errors were added to the model. I can't find any errors, however the message above persists.
This is my model
module Surveyor
class Answer
attr_accessor :question, :value
def initialize(params)
#question = params.fetch(:question)
#value = params.fetch(:value)
end
end
end
And the class Question
module Surveyor
class Question
attr_accessor :title, :type
def initialize(params)
#title = params.fetch(:title, nil)
#type = params.fetch(:type)
end
end
end
And this is the test I am attempting to pass
RSpec.describe Surveyor::Answer, '03: Answer validations' do
let(:question) { double(Surveyor::Question, type: 'rating') }
context "question validation" do
context "when the answer has a question" do
subject { described_class.new(question: question, value: 5) }
it { should be_valid }
end
end
Is my understanding of 'valid?' correct? Am I able to look at 'valid?' and perhaps see where I'm going wrong?
RSpec doesn't actually have a matcher called be_valid, instead it has some dynamic predicate matchers:
For any predicate method, RSpec gives you a corresponding matcher. Simply prefix the
method with be_ and remove the question mark. Examples:
expect(7).not_to be_zero # calls 7.zero?
expect([]).to be_empty # calls [].empty?
expect(x).to be_multiple_of(3) # calls x.multiple_of?(3)
so by calling it { should be_valid }, your subject has to respond to a valid? method. If you're testing an ActiveRecord model, those have a valid? method, but your model does not. So, if you want to test that your Answer is valid, you need to decide "what is a valid answer?" and write a method that checks for those conditions. If you want an API similar to Rails model, you might be interested in using ActiveModel::Validations

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

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.

RSpec Testing Controller with Model.create

I'm trying to test a controller to ensure that only an authorized party can view the correct child object using RSpec. I cant figure out what I'm doing wrong as I'm getting this error:
ActiveRecord::RecordInvalid: Validation failed: Company can't be blank
I have a Plan object and a Company object. The Store can have many plans (think of a pest control Company). I want to test that given a known scenario I can retrieve the plan fo the Company (assuming there is only one).
The Plan looks like this:
class Plan < ActiveRecord::Base
before_save :default_values
# Validation
validates :amount, :presence => true
validates :company, :presence => true
# Plans belong to a particular company.
belongs_to :company, :autosave => true
scope :find_all_plans_for_company, lambda {
|company| where(:company_id => company.id)
}
# Other code ...
end
The Company looks like this:
class Company < ActiveRecord::Base
validates :name, :presence => true
validates :phone1, :presence => true
validates_format_of :phone1, :phone2,
:with => /^[\(\)0-9\- \+\.]{10,20}$/,
:message => "Invalid phone number, must be 10 digits. e.g. - 415-555-1212",
:allow_blank => true,
:allow_nil => true
has_many :users
has_many :plans
end
.. controller looks like this
def index
#plans = Plan.find_all_plans_for_company(current_user.company)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #plans }
end
end
.. and my RSpec test looks like this (excuse me if its full of gimmickery, I'm just splunking around with it and cannot get it to work).
describe PlansController do
def valid_attributes
{
:company_id => 1,
:amount => 1000
}
end
describe "GET index" do
it "should return the Plans for which this users company has" do
#company = mock_model(Company, :id => 1, :name => "Test Company", :phone1 => "555-121-1212")
Company.stub(:find).with(#company.id).and_return(#company)
controller.stub_chain(:current_user, :company).and_return(#company)
plan = Plan.create! valid_attributes
get :index, {}
assigns(:plans).should eq([plan])
end
# Other tests ...
end
end
The problem is, when I try this (or any of the crazy other variants I've tried) I get this error:
ActiveRecord::RecordInvalid: Validation failed: Company can't be blank
I'm not sure why this is happening as I thought the Company.stub call would handle this for me. But apparently not.
What am I missing here and what am I doing wrong? How can I get this test to pass?
Let's peel back the layers on this spec, to make sure things make sense (and to make sure I understand what's going on). First, what are you testing?
it "should return the Plans for which this users company has" do
...
assigns(:plans).should eq([plan])
So you want to check that the plans associated with the company of the current user are assigned to #plans. We can stub or mock out everything else.
Looking at the controller code, we have:
def index
#plans = Plan.find_all_plans_for_company(current_user.company)
What do we need to get this to work, without hitting the database and without depending on the models?
First of all, we want to get a mock company out of current_user.company. This is what these two lines in your spec code do:
#company = mock_model(Company, :id => 1, :name => "Test Company", :phone1 => "555-121-1212")
controller.stub_chain(:current_user, :company).and_return(#company)
This will cause current_user.company to return the mock model #company. So far so good.
Now to the class method find_all_plans_for_company. This is where I'm a bit confused. In your spec, you stub the find method on Company to return #company for id = 1.
But really, wouldn't it suffice just to do something like this in your controller code?:
#plans = current_user.company.plans
If you did it this way, then in your test you could just mock a plan, and then return it as the plans association for your mock company:
#plan = mock_model(Plan)
#company = mock_model(Company, :plans => [ #plan ])
controller.stub_chain(:current_user, :company).and_return(#company)
Then the assignment should work, and you don't need to actually create any model or hit the database. You don't even need to give your mock company an id or any other attributes, which anyway are irrelevant to the spec.
Maybe I'm missing something here, if so please let me know.
Why do you need to mock?
My standard testing setup is to use Database Cleaner which clears out the database from any records created during tests. In this way, the tests are run with real database records which are consequently deleted from the test database after each test.
You might also like taking a look at Factory Girl for creating instances of your models during testing (makes it easy to create 10 company records, for example).
See:
http://rubygems.org/gems/database_cleaner
http://rubygems.org/gems/factory_girl
I have three thoughts coming up that could resolve your issue:
Try adding attr_accessible :company_id to Plan class.
Because mock_model does not actually save to the database when you create a Plan with company_id of 1 it fails validation since it is not present in the database.
Ensure before_save :default_values in Plan class does not mess with company_id attribute of the newly created instance.

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.

"Here methods" in Ruby?

I'm writing a few helpers to DRY up my tests. I pictured something like:
class ActiveSupport::TestCase
def self.test_presence_validation_of model, attribute
test "should not save #{model.to_s} with null #{attribute.to_s}", <<-"EOM"
#{model.to_s} = Factory.build #{model.to_sym}, #{attribute.to_sym} => nil
assert !#{model.to_s}.save, '#{model.to_s.capitalize} with null #{attribute.to_s} saved to the Database'
EOM
# Another one for blank attribute.
end
end
So that this:
class MemberTest < ActiveSupport::TestCase
test_presence_validation_of :member, :name
end
Executes exactly this at MemberTest class scope:
test 'should not save member with null name' do
member = Factory.build :member, :name => nil
assert !member.save, 'Member with null name saved to the Database'
end
Is it possible to do it this way (with a few adaptations, of course; I doubt my "picture" works), or do I have to use class_eval?
Have you seen Shoulda? It's great for testing common Rails functionality such as validations, relationships etc. https://github.com/thoughtbot/shoulda-matchers
In this case, it seems class_eval is necessary since I want to interpolate variable names into actual code.
Illustrated here.

Resources