Testing with Rspec codeschool level 3 challenge 5 - ruby

I have been battling this test way too long and I am not sure where I am stuck. Here is the model I am trying to test:
class Zombie < ActiveRecord::Base
attr_accessible :iq
validates :name, presence: true
def genius?
iq >= 3
end
def self.genius
where("iq >= ?", 3)
end
end
Here is what I am working with to start:
describe Zombie do
context "with high iq" do
let(:zombie) { Zombie.new(iq: 3, name: 'Anna') }
subject { zombie }
it "should be returned with genius" do
Zombie.genius.should include(zombie)
end
it "should have a genius count of 1" do
Zombie.genius.count.should == 1
end
end
end
This is the part of the refactor that is working:
it { should be_genius }
#it "should have a genius count of 1" do
# Zombie.genius.count.should == 1
#end
Here is where I am currently stuck at with the refactor:
describe Zombie do
context "with high iq" do
let!(:zombie) { Zombie.new(iq: 3, name: 'Anna') }
subject { zombie }
it {should include("zombie")}
it { should be_genius }
end
end
According to the examples this should work, but no matter what I try it keeps bombing on the include. I know I am missing something lame here. Thoughts or tips anyone?
Current Error Message:
Failures:
1) Zombie with high iq
Failure/Error: it {should include("zombie")}
NoMethodError:
undefined method `include?' for #<Zombie:0x00000006792380>
# zombie_spec.rb:7:in `block (3 levels) '
Finished in 0.12228 seconds
2 examples, 1 failure
Failed examples:
rspec zombie_spec.rb:7 # Zombie with high iq

You need to add the ! to the let and change new to create in order to save the record.
describe Zombie do
context "with high iq" do
let!(:zombie) { Zombie.create(iq: 3, name: 'Anna') }
subject { zombie }
it "should be returned with genius" do
Zombie.genius.should include(zombie)
end
it "should have a genius count of 1" do
Zombie.genius.count.should == 1
end
end
end

I'm not sure what examples you are referring to that suggest your refactor should work, but the implicit subject zombie used in your first refactored example is an ActiveRecord instance and the include matcher you're using is intended to be used with a string, array or hash as described in https://www.relishapp.com/rspec/rspec-expectations/v/2-0/docs/matchers/include-matcher.
As for your second refactored example, I gather it's working because you've only indicated a problem with the include.

Related

Why does changing the order of 'it' and 'subject' in RSpec change my test result?

The class being tested qa.rb contains the code:
class QA
def initialize(bugs: 0)
#bugs = bugs
end
def speak
"Hello!"
end
def happy?
#bugs > 0
end
def debug
#bugs = 0
end
end
The RSpec file qa_spec.rb contains the code:
require 'rspec'
require_relative 'qa'
RSpec.describe QA do
describe '#happy?' do
context 'when bugs are more than 0' do
it 'returns true' do
subject { described_class.new(bugs: 1) }
expect(subject).to be_happy
end
end
end
end
The test fails when I run it, and gives me this error:
PS C:\Users\Jobla\repos\TDD> rspec qa_spec.rb
F
Failures:
1) QA#happy? when bugs are more than 0 returns true
Failure/Error: expect(subject).to be_happy
expected `#<QA:0x2e0d640 #bugs=0>.happy?` to return true, got false
# ./qa_spec.rb:9:in `block (4 levels) in <top (required)>'
Finished in 0.02999 seconds (files took 0.16995 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./qa_spec.rb:7 # QA#happy? when bugs are more than 0 returns true
However, when I edit qa_spec.rb and I swap the it and subject lines, the test suddenly passes:
require 'rspec'
require_relative 'qa'
RSpec.describe QA do
describe '#happy?' do
context 'when bugs are more than 0' do
subject { described_class.new(bugs: 1) } #swapped with line below
it 'returns true' do #swapped with line above
expect(subject).to be_happy
end
end
end
end
Tests pass:
PS C:\Users\Jobla\repos\TDD> rspec qa_spec.rb
.
Finished in 0.01003 seconds (files took 0.17993 seconds to load)
1 example, 0 failures
Please could someone explain why does swapping the it and subject lines change the result of the test?
subject is designed to be set in context or describe block, but not in it.
If you do not set subject before it then subject would be set automatically by calling new without parameters on described_class. bugs will be set to default 0. After that, you call it with a block subject { described_class.new(bugs: 1) } inside it, it's the same as if you call described_class.new { described_class.new(bugs: 1) } because subject inside it is an instance of QA class.

Making a method return a specific value (that would otherwise be random) in Rspec

I'm trying to write an rspec test that would assume a method will return a specific value when it would normally return a random number between 1 and 10.
Here is the code in my lib directory:
def take_off(plane)
fail "Bad weather today. Cannot take off." if stormy_weather? > 8
plane.take_off_plane
planes.delete(plane)
end
def stormy_weather?
rand(10)
end
And here is my test in Rspec:
it 'raises an error when weather is stormy' do
plane = double(:plane)
stormy_weather = 9
allow(plane).to receive(:take_off_plane)
expect{ subject.take_off(plane) }.to raise_error("Bad weather today. Cannot take off.")
end
Thank you in advance to anyone who helps!
You can specify the returned value with and_return:
allow(subject).to receive(:stormy_weather?).and_return(9)

How can I do a has_many relationship just using Ruby, i.e. not using Rails?

I don't mean how do I include ActiveRecord, but let me explain.
I want to have a Game with a difficultyLevelID and a DifficultyLevel Object.
In Rails and ActiveRecord (that's what I am familiar with) these would be tables and I would have the has_many and belongs_to methods and then I could just use the difficultyLevelID to get things, so difficulty level could be Game.difficulty_level.name
If I am just doing a Ruby program with no database and I want to use that relationship, i.e. I want Game to have an ID for difficulty level and the level name itself to be in a difficulties class, how do I do that (create, maintain and query the relationship) just with Ruby so that I can say get the game difficulty level name?
There was no answer in 20 hours so I've posted my own.
class Soduko
attr_accessor :name, :rows, :columns, :difficulty_level
def initialize // will probably move to parameters as defaults.
#rows= 9
#columns= 9
#name= 'My Soduko'
#difficulty_level= 'Medium'
end
def initial_number_count
DifficultyLevel.start_with_numbers('Medium')
end
end
class DifficultyLevel
def self.start_with_numbers(difficulty_level)
case difficulty_level
when 'Easy'
then 30
when 'Medium'
then 20
when 'Hard'
then 10
else 20
end
end
end
and of course the tests:
require './soduko'
describe Soduko, '.new' do
before { #soduko_board = Soduko.new }
it "Should allow for a new Board with 9 rows (default) to be created" do
#soduko_board.rows.should == 9
end
it "Should allow for a new Board with 9 columns (default) to be created" do
#soduko_board.columns.should == 9
end
it "should have a default difficulty level of 'Medium'" do
#soduko_board.difficulty_level.should == 'Medium'
end
it "should have 10 initial numbers" do
#soduko_board.initial_number_count.should == 20
end
end
describe DifficultyLevel, '.new' do
it "should exist" do
#difficulty_level = DifficultyLevel.new
end
# More to be added...
end

RSpec 2 speccing a static ActiveRecord method

I've got a pretty basic static method on an ActiveRecord model:
#./app/models/comic.rb
class Comic < ActiveRecord::Base
class << self
def furthest
Comic.maximum(:comic_id) || 0
end
end
end
When executing Comic.furthest in the Rails console it returns 0 as I expect. The problem is I am trying to spec this behavior for both the presence and absence of records:
#./spec/app/models/comic_spec.rb
require 'spec_helper'
describe Comic do
describe "#furthest" do
subject { Comic.furthest }
context "when there are no rows in the database" do
it { should == 0 }
end
context "when there are rows in the database" do
before do
Factory.create(:comic, :comic_id => 100)
Factory.create(:comic, :comic_id => 99)
end
it { should == 100 }
end
end
end
All of this appears very basic and straightforward, however my specs are failing with the message:
1) Comic#furthest when there are no rows in the database
Failure/Error: it { should == 0 }
expected: 0
got: nil (using ==)
# ./spec/models/comic_spec.rb:8:in `block (4 levels) in <top (required)>'
Even if I change furthest to simply:
def furthest
0
end
I still get nil (using ==).
The second spec, it { should == 100 } passes with the original Comic.maximum(:comic_id) || 0 definition, as if the Factory.create invocations are required for #furthest to not return nil.
What am I doing wrong?
I am fairly confident this was a problem with me using the p180 release of Ruby 1.9.2 with custom patches to improve require performance. After upgrading to p290 this problem is gone.

rspec mock question

I am trying to Mock an object that is being passed into another object, and am having no success. Can someone show me what I am doing wrong?
class Fetcher
def download
return 3
end
end
class Reports
def initialize(fetcher)
#fetcher = fetcher
end
def status
#fetcher.download
end
end
describe Reports do
before(:each) do
end
it "should get get status of 6" do
Fetcher.should_receive(:download).and_return(6)
f = Reports.new(Fetcher.new)
f.status.should == 6
end
end
The spec still reports status returning 3, not my intended 6.
Certainly I am missing something here. Any thoughts?
In the test, what I think you're trying to do is this (I think)
it '...' do
some_fetcher = Fetcher.new
some_fetcher.should_receive(:download).and_return(6)
f = Reports.new(some_fetcher)
f.status.should == 6
end
when you say Fetcher.should_receive(:download), you're saying the CLASS should receive the call 'download', instead of INSTANCES of the class...
Hope this helps! If it's not clear, just let me know!
An updated based on the new syntax.
subject(:reports) { described_class.new(fetcher) }
let(:fetcher} { double("Fetcher") }
describe "#status" do
it "gets status of 6" do
fetcher.stub(download: 6)
expect(reports.status).to == 6
end
end

Resources