Undefined local variable or method in Rspec test? - ruby

Here is my class
class Hero
attr_reader :strength, :health, :actions
def initialize(attr = {})
#strength = attr.fetch(:strength, 3)
#health = attr.fetch(:health, 10)
#actions = attr.fetch(:actions, {})
#dicepool = attr.fetch(:dicepool)
end
def attack(monster)
#dicepool.skill_check(strength, monster.toughness)
end
end
And these are my tests
require 'spec_helper'
require_relative '../../lib/hero'
describe Hero do
let(:dicepool) {double("dicepool")}
describe "def attributes" do
let(:hero){Hero.new dicepool: dicepool}
it "has default strength equal to 3" do
expect(hero.strength).to eq(3)
end
it "has default health equal to 10" do
expect(hero.health).to eq(10)
end
it "can be initialized with custom strength" do
hero = Hero.new strength: 3, dicepool: dicepool
expect(hero.strength).to eq(3)
end
it "can be initialized with custom health" do
hero = Hero.new health: 8, dicepool: dicepool
expect(hero.health).to eq(8)
end
describe "attack actions" do
let(:attack_action) {double("attack_action") }
let(:hero) {Hero.new dicepool: double("dicepool"), actions: {attack: attack_action} }
it "has attack action"
expect(hero.actions[:attack]).to eq(attack_action)
end
end
end
I keep on getting
in `block (3 levels) in ': undefined local variable or method 'hero' for
RSpec::ExampleGroups::Hero::DefAttributes::AttackActions:Class (NameError)
and I don't know why. This is my first day of writing Rspec tests so please be nice...

You have a typo in your last test, you forgot the word do:
it "has attack action" do
expect(hero.actions[:attack]).to eq(attack_action)
end
Everything passes once added.

You are not passing a block to the it method (you missing both do and end at the end).
it "has attack action"
^^^
The correct code should look like this:
describe Hero do
let(:dicepool) {double("dicepool")}
describe "def attributes" do
let(:hero){Hero.new dicepool: dicepool}
it "has default strength equal to 3" do
expect(hero.strength).to eq(3)
end
it "has default health equal to 10" do
expect(hero.health).to eq(10)
end
it "can be initialized with custom strength" do
hero = Hero.new strength: 3, dicepool: dicepool
expect(hero.strength).to eq(3)
end
it "can be initialized with custom health" do
hero = Hero.new health: 8, dicepool: dicepool
expect(hero.health).to eq(8)
end
describe "attack actions" do
let(:attack_action) {double("attack_action") }
let(:hero) {Hero.new dicepool: double("dicepool"), actions: {attack: attack_action} }
it "has attack action" do
expect(hero.actions[:attack]).to eq(attack_action)
end
end
end
end

Related

rspec vote validations error: must pass hash as an argument

I am trying to write a spec code for a vote_spec model. Not sure what exactly it is I'm doing wrong. I think it may be in the first #vote attribute in the before block.
This is how the validations should work:
Console
v = Vote.new(value: 1)
v.valid? #=> true
v2 = Vote.new(value: -1)
v2.valid? #=> true
v3 = Vote.new(value: 2)
v3.valid? #=> false
This is the error:
Failure/Error: #vote = Vote.create(:post_id)
ArgumentError:
When assigning attributes, you must pass a hash as an argument.
This is my vote_spec.rb
require 'rails_helper'
describe Vote do
describe "validations" do
before do
#vote = Vote.create(:post_id)
#vote.create(value: 1)
#vote.create(value: -1)
end
describe "first_validation" do
it "only allows -1 as a value" do
expect(#vote.first_validation).to eq(-1)
end
end
describe "second_validation" do
it "only allows 1 as a value" do
expect(#vote.second_validation).to eq(1)
end
end
end
end
If you want to test validation, maybe you could do something like this:
describe "validations" do
it 'is valid if the value is 1' do
expect(Vote.new(value: 1)).to be_valid
end
it 'is valid if the value is -1' do
expect(Vote.new(value: -1)).to be_valid
end
[-3, 0, 4].each do |invalid_value|
it "is not valid if the value is #{invalid_value}" do
expect(Vote.new(value: invalid_value)).not_to be_valid
end
end
end
Amanjot,
Also as Sasha mentioned in the comments. You can just continue with this below code I think
require 'rails_helper'
describe Vote do
describe "validations" do
before do
#first_vote = Vote.create(value: -1) # Vote.new(value: -1) - should try this too
#second_vote= Vote.create(value: 1) # Vote.new(value: 1) - should try this too
end
describe "first_validation" do
it "only allows -1 as a value" do
expect(#first_vote.value).to eq(-1)
end
end
describe "second_validation" do
it "only allows 1 as a value" do
expect(#second_vote.value).to eq(1)
end
end
end
Try out something like this. You would need to use the create action on the Vote model.

Rspec to have(n).items undefined method

I'm trying to follow a guide on code.tuts and I keep getting an error.
Here is my Library spec:
require 'spec_helper'
describe Library do
before :all do
lib_arr = [
Book.new("JavaScript: The Good Parts", "Douglas Crockford", :development),
Book.new("Dont Make me Think", "Steve Krug", :usability),
]
File.open "books.yml", "w" do |f|
f.write YAML::dump lib_arr
end
end
before :each do
#lib = Library.new "books.yml"
end
describe "#new" do
context "with no parameters" do
it "has no book" do
lib = Library.new
expect(lib).to have(0).books
end
end
context "with a yaml file name parameters" do
it "has two books" do
expect(#lib).to_have(0).books
end
end
end
it "returns all the books in a given category" do
expect(#lib.get_books_in_category(:development).length).to eql 1
end
it "accepts new books" do
#lib.add_book(Book.new("Designing for the Web", "Mark Boulton", :design))
expect(#lib.get_book("Designing for the Web")).to be_an_instance_of Book
end
it "saves the library" do
books = #lib.books.map { |book| book.title}
#lib.save
lib2 = Library.new 'books.yml'
books2 = lib2.books.map { |book| book.title }
expect(books).to eql books2
end
end
I'm getting that have is undefined. I've figured out it's my lines
expect(#lib).to have(0).books
expect(lib).to have(0).books
Is my syntax out of date? I've googled and I can't find it.
The have/have_exactly, have_at_least and have_at_most matchers were removed from RSpec 3. They're now in the separate rspec-collection_matchers gem.
Or, as zishe says, instead of installing the gem, you can just use eq instead of have/have_exactly, and be >= instead of have_at_least and be <= instead of have_at_most.
Source: http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3

wrong number of arguments ruby rspec

I'm trying to write unit tests for my code using rspec. I keep getting a "wrong number of arguments" error:
class MyClass
attr_accessor :env, :company,:size, :role, :number_of_hosts,:visability
def initialize(env, company, size, role, number_of_hosts, visability)
#env, #company, #size, #role, #number_of_hosts, #visability = env, company, size, role, number_of_hosts, visability
end
end
And here are my tests:
require_relative "../lib/MyClass.rb"
describe MyClass do
it "has an environment" do
MyClass.new("environment").env.should respond_to :env
end
it "has a company" do
MyClass.new("company").company.should respond_to :company
end
...
When I run rspec I get:
1) MyClass has an environment
Failure/Error: MyClass.new("environment").env.should respond_to :env
ArgumentError:
wrong number of arguments (1 for 6)
# ./lib/MyClass.rb:4:in `initialize'
# ./spec/MyClass_spec.rb:5:in `new'
# ./spec/MyClass_spec.rb:5:in `block (2 levels) in <top (required)>'
...
What am I missing?
EDIT
Sergio helped thanks...however
Sergio's answer worked...although I still have a further question:
Given the Class:
class Team
attr_accessor :name, :players
def initialize(name, players = [])
raise Exception unless players.is_a? Array
#name = name
raise Exception if #name && has_bad_name
#players = players
end
def has_bad_name
list_of_words = %w{crappy bad lousy}
list_of_words - #name.downcase.split(" ") != list_of_words
end
def favored?
#players.include? "George Clooney"
end
end
and spec...
require_relative "../lib/team.rb"
describe Team do
it "has a name" do
Team.new("random name").should respond_to :name
end
it "has a list of players" do
Team.new("random name").players.should be_kind_of Array
end
...
The tests pass without the same error...(This works fine: Team.new("random name"))
Any explanation?
Here is the error MyClass.new("environment"). As you have written def initialize(env, company, size, role, number_of_hosts, visability). So you should pass 6 parameters when you are calling MyClass#new method. But in practice you pass only one which is "environment". Thus you got the legitimate error - wrong number of arguments (1 for 6).

Testing after_create with rspec

I have code in my model.
class Foo < ActiveRecord::Base
after_create :create_node_for_foo
def create_node_for_user
FooBar.create(id: self.id)
end
end
and have code in rspec of Foo model
describe Foo do
let (:foo) {FactoryGirl.create(:foo)}
subject { foo }
it { should respond_to(:email) }
it { should respond_to(:fullname) }
it "have mass assignable attributes" do
foo.should allow_mass_assignment_of :email
foo.should allow_mass_assignment_of :fullname
end
it "create node in graph database" do
foo1 = FactoryGirl.create(:foo)
FooBar.should_receive(:create).with(id: foo1.id)
end
end
but my test is failing with message
Failures:
1) Foo create node in graph database on
Failure/Error: FooBar.should_receive(:create).with(id: foo1.id)
(<FooBar (class)>).create({:id=>18})
expected: 1 time
received: 0 times
What might be wrong?
Okay got around with problem
changed this
it "create node in graph database" do
foo1 = FactoryGirl.create(:foo)
FooBar.should_receive(:create).with(id: foo1.id)
end
to
it "create node in graph database" do
foo1 = FactoryGirl.build(:foo)
FooBar.should_receive(:create).with(id: foo1.id)
foo1.save
end
A bit late to the party but actually the above won't work. You can create a custom matcher to do it:
class EventualValueMatcher
def initialize(&block)
#block = block
end
def ==(value)
#block.call == value
end
end
def eventual_value(&block)
EventualValueMatcher.new(&block)
end
Then in your spec do:
it "create node in graph database" do
foo1 = FactoryGirl.build(:foo)
FooBar.should_receive(:create).with(eventual_value { { id: foo1.id } })
foo1.save
end
This means that the mock will not evaluate the block until after the fact and it has actually been set.
In case it helps someone, an updated version of Dan Draper solution to use a custom matcher with a block, would be like this:
# spec/support/eventual_value_matcher.rb
RSpec::Matchers.define :eventual_value do |expected|
match do |actual|
actual == expected.call
end
end
And usage:
require "support/eventual_value_matcher" # or you can do a global require on the rails_helper.rb file
it "xxx" do
foo1 = FactoryGirl.build(:foo)
expect(FooBar).to receive(:create).with(eventual_value(proc { foo1.id }))
foo.save!
end

RSpec: How do I refactor a group of tests that are repeated and the only thing that changes is the subject and expectations

I have a test suite that resembles the situation I describe with the following code. There are two contexts that define the subject. The subject is similar, the same kind of object, but with different values.
Over that subject I run two tests. One test is exactly the same for both and the other is different.
Suggest a refactor that would eliminate duplication, besides the obvious 'move the code to a method', which I don't like because it looses clarity.
require 'rspec'
describe "tests over numbers" do
context 'big numbers' do
subject { 5000 }
describe "#to_string" do
its(:to_s) {should be_a(String)}
end
describe "#+1" do
it "+1" do
sum = subject+1
sum.should == 5001
end
end
end
context 'small numbers' do
subject { 100 }
describe "#to_string" do
its(:to_s) {should be_a(String)}
end
describe "#+1" do
it "+1" do
sum = subject+1
sum.should == 101
end
end
end
end
Maybe shared examples is the way to go?
shared_example "numbers" do
describe "#to_string" do
it "should convert to a string" do
example.to_s.should be_a(String)
end
end
describe "#+1" do
it "should increment" do
sum = example+1
sum.should == example.next
end
end
end
describe "big_numbers" do
it_behaves_like "numbers" do
let(:example) { 5000 }
end
end
describe "small_numbers" do
it_behaves_like "numbers" do
let(:example) { 100 }
end
end
[5000, 100].each do |my_test|
subject { my_test }
describe "#to_string" do
its(:to_s) {should be_a(String)}
end
describe "#+1" do
it "+1" do
sum = subject+1
sum.should == my_test + 1
end
end
end

Resources