rspec - wrong number of arguments (0 for 1..2) - ruby

Given this code:
class Game
def self.game_board
return [[][][]]
end
def self.empty_output
'_|_|_'+
'_|_|_'+
' | |'
end
end
and these tests:
describe 'It should display a grid' do
it 'should have empty output' do
Game.empty_output.should ==
'_|_|_'+
'_|_|_'+
' | |'
end
it 'should have an empty array for the game ' do
Game.game_board.should ==
[[][][]]
end
end
why does the test fail that tries to return the array of arrays with the error
.F
Failures:
1) "It should display a grid should be empty
Failure/Error: return [[][][]]
ArgumentError:
wrong number of arguments (0 for 1..2)
# ./checkers_rspec.rb:4:in `[]'
# ./checkers_rspec.rb:4:in `game_board'
# ./checkers_rspec.rb:24:in `block (2 levels) in <top (required)>'
Finished in 0.00121 seconds
2 examples, 1 failure

[[][][]] should hae been [[],[],[]] in both cases.

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.

Added Timeout to method, and now the first spec always fails

I recently added a timeout to a method that is used by about a dozen different commands in the class. Here is the method:
def exec_mc!(command)
begin
Timeout.timeout(30.minutes) do
library_motion_logger ||= ::Logging.logger["#library_logger.name}::Motion"]
library_motion_logger.info("Executing mc command 'mc #{command}' ...")
result = rcm_ssh.execute("/RCM_code/mc #{command}")
# TODO: Can this be the result.success method instead, which checks for empty stderr also
# Match the time pattern from mc
success = result.exit_code.zero?
MotionCommandResult.new(success, result.execution_time, result.stdout, result.stderr)
end
rescue Timeout::Error
# Something is wrong. Fail out now.
library_motion_logger.warn("Timed out while waiting for mc command '#{command}'")
false
end
end
Now when I run rspec, the first spec fails no matter which one is ran first. If I run a single spec, that also fails. Here is what I am seeing as the failure:
1) Library::Motion::MotionCommander#gather behaves like basic motion command with successful result should receive execute("/RCM_code/mc --gather", *(any args)) 1 time
Failure/Error: #library_gem_logger ||= ::Logging.logger[LIBRARY_GEM_LOGGER_ROOT_DEFAULT]
Timeout::Error:
execution expired
As you can see, it says that the error occurs while trying to get the logger in the very first line of my begin block. If I remove this line, I get the same error on the next line. So basically I am always getting an error on the first line of the first spec, no matter what. I have tried moving things around and this has held true. What can I do to fix this?
Edit: Here are some of the specs
def stub_and_expect_mc(exits = [], stderrs = [], stdouts = [])
full_command = "/RCM_code/mc #{command}"
ssh_stub_and_expect(full_command, exits, stderrs, stdouts)
end
shared_examples 'basic motion command' do
context 'with successful result' do
before :each do
stub_and_expect_mc(0)
end
it { is_expected.to have_attributes(success: true) }
end
context 'with unsuccessful result' do
before :each do
stub_and_expect_mc(1)
end
it { is_expected.to have_attributes(success: false) }
end
end
describe '#random_slot_to_slot' do
let(:command) { '--randomslot' }
subject { #library.random_slot_to_slot }
it_behaves_like 'basic motion command'
end
describe '#drawer_to_drawer' do
# Extra space before --srcdrawer since no mover number is specified
let(:command) { "--drawertodrawer --srcdrawer #{src_drawer} --destdrawer #{dest_drawer} --force" }
let(:src_drawer) { SpectraLibrary::Motion::HydraDrawer.new(0, 0, 1, 0, '', '', 1) }
let(:dest_drawer) { SpectraLibrary::Motion::HydraDrawer.new(0, 0, 2, 0, '', '', 2) }
subject { #library.drawer_to_drawer(src_drawer, dest_drawer) }
it_behaves_like 'basic motion command'
end
Edit 2: Here are the last three times I ran specs, with the rescue removed so you can see the actual failure (otherwise it just shows the result was false)
Failures:
1) SpectraLibrary::Motion::MotionCommander#gather behaves like basic motion command with successful result should receive execute("/RCM_code/mc --gather", *(any args)) 1 time
Failure/Error: #library_gem_logger ||= ::Logging.logger[LIBRARY_GEM_LOGGER_ROOT_DEFAULT]
Timeout::Error:
execution expired
Shared Example Group: "basic motion command" called from ./spec/lib/spectra_library/motion/motion_commander_spec.rb:80
# ./lib/spectra_library/logging.rb:15:in `library_gem_logger'
# ./lib/spectra_library/logging.rb:28:in `library_logger'
# ./lib/spectra_library/logging.rb:35:in `library_motion_logger'
# ./lib/spectra_library/motion/motion_commander.rb:150:in `block in exec_mc!'
# ./lib/spectra_library/motion/motion_commander.rb:148:in `exec_mc!'
# ./lib/spectra_library/motion/motion_commander.rb:95:in `gather'
# ./spec/lib/spectra_library/motion/motion_commander_spec.rb:79:in `block (3 levels) in <top (required)>'
# ./spec/lib/spectra_library/motion/motion_commander_spec.rb:20:in `block (4 levels) in <top (required)>'
Finished in 0.09762 seconds (files took 0.72568 seconds to load)
30 examples, 1 failure
Failures:
1) SpectraLibrary::Motion::MotionCommander#drawer_to_drawer behaves like basic motion command with successful result should receive execute("/RCM_code/mc --drawertodrawer --srcdrawer #<SpectraLibrary::Motion::HydraDrawer:0x000055710f024e20> --destdrawer #<SpectraLibrary::Motion::HydraDrawer:0x000055710f024d30> --force", *(any args)) 1 time
Failure/Error: #library_gem_logger ||= ::Logging.logger[LIBRARY_GEM_LOGGER_ROOT_DEFAULT]
Timeout::Error:
execution expired
Shared Example Group: "basic motion command" called from ./spec/lib/spectra_library/motion/motion_commander_spec.rb:42
# ./lib/spectra_library/logging.rb:15:in `library_gem_logger'
# ./lib/spectra_library/logging.rb:28:in `library_logger'
# ./lib/spectra_library/logging.rb:35:in `library_motion_logger'
# ./lib/spectra_library/motion/motion_commander.rb:150:in `block in exec_mc!'
# ./lib/spectra_library/motion/motion_commander.rb:149:in `exec_mc!'
# ./lib/spectra_library/motion/motion_commander.rb:29:in `drawer_to_drawer'
# ./spec/lib/spectra_library/motion/motion_commander_spec.rb:41:in `block (3 levels) in <top (required)>'
# ./spec/lib/spectra_library/motion/motion_commander_spec.rb:20:in `block (4 levels) in <top (required)>'
Finished in 0.08525 seconds (files took 0.75393 seconds to load)
30 examples, 1 failure
Failures:
1) SpectraLibrary::Motion::MotionCommander#drawer_to_drawer behaves like basic motion command with successful result should receive execute("/RCM_code/mc --drawertodrawer --srcdrawer #<SpectraLibrary::Motion::HydraDrawer:0x000055ef29d2efd8> --destdrawer #<SpectraLibrary::Motion::HydraDrawer:0x000055ef29d2ef38> --force", *(any args)) 1 time
Failure/Error: #library_gem_logger ||= ::Logging.logger[LIBRARY_GEM_LOGGER_ROOT_DEFAULT]
Timeout::Error:
execution expired
Shared Example Group: "basic motion command" called from ./spec/lib/spectra_library/motion/motion_commander_spec.rb:42
# ./lib/spectra_library/logging.rb:15:in `library_gem_logger'
# ./lib/spectra_library/logging.rb:28:in `library_logger'
# ./lib/spectra_library/logging.rb:35:in `library_motion_logger'
# ./lib/spectra_library/motion/motion_commander.rb:150:in `block in exec_mc!'
# ./lib/spectra_library/motion/motion_commander.rb:149:in `exec_mc!'
# ./lib/spectra_library/motion/motion_commander.rb:29:in `drawer_to_drawer'
# ./spec/lib/spectra_library/motion/motion_commander_spec.rb:41:in `block (3 levels) in <top (required)>'
# ./spec/lib/spectra_library/motion/motion_commander_spec.rb:20:in `block (4 levels) in <top (required)>'
Finished in 0.10332 seconds (files took 0.73916 seconds to load)
30 examples, 1 failure

Rspec not finding class methods

I'm writing some tests for my backend jobs and I'm having a weird issue with rspec not finding my methods.
I wrote a simple class & test to illustrate the issue :
app/interactors/tmp_test.rb :
class TmpTest
def call
a = 10
b = 5
b.substract_two
return a + b
end
def substract_two
c = self - 2
return c
end
end
spec/interactors/tmp_test.rb :
require 'rails_helper'
describe TmpTest do
context 'when doing the substraction' do
it 'return the correct number' do
expect(described_class.call).to eq(13)
end
end
end
output:
TmpTest
when doing the substraction
return the correct number (FAILED - 1)
Failures:
1) TmpTest when doing the substraction return the correct number
Failure/Error: expect(described_class.call).to eq(13)
NoMethodError:
undefined method `call' for TmpTest:Class
# ./spec/interactors/tmp_test.rb:6:in `block (3 levels) in <top (required)>'
Finished in 0.00177 seconds (files took 1.93 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/interactors/tmp_test.rb:5 # TmpTest when doing the substraction return the correct number
It's not a class method, it's an instance method. Your test should look like this:
describe TmpTest do
subject(:instance) { described_class.new }
context 'when doing the subtraction' do
it 'returns the correct number' do
expect(instance.call).to eq(13)
end
end
end
This is a complete mess. Corrected version with comments:
class TmpTest
def call
a = 10
b = 5
# b.substract_two # why do you call method of this class on b?!
a + subtract_two(b)
end
def substract_two(from)
from - 2
end
end
Also: don’t use return in the very last line of the method.

CSV Import RSpec error in Ruby

I am working on an assignment in Ruby.. I had to write RSPEC tests for a method that removes an entry from my address_book app.. and also had to write a test and a method for importing 5 entries from a CSV. In my remove_entry method when I run the Specs it says that I have an undefined method of delete. I have already asked a couple fellow ruby devs and they could not figure it out at first glance. The next error is that when I run the tests for my CSV imports.. The data is not being imported in the proper order. I have spent several hours walking through my code over and over and researching in attempts to fix this.. I am at my wits end.. Any help would be appreciated!
address_book.rb
require_relative 'entry'
require "csv"
class AddressBook
attr_accessor :entries
def initialize
#entries = []
end
def add_entry(name,phone_number,email)
index = 0
#entries.each do |entry|
if name < entry.name
break
end
index += 1
end
#entries.insert(index, Entry.new(name, phone_number, email))
end
def import_from_csv(file_name)
csv_text = File.read(file_name)
csv = CSV.parse(csv_text, headers: true, skip_blanks: true)
csv.each do |row|
row_hash = row.to_hash
add_entry(row_hash["name"], row_hash["phone_number"], row_hash["email"])
#can you clarify what the above is doing
#is the format of row_hash["name"] because it is iterating over a hash or because it is an array?
end
end
def remove_entry(name,phone_number,email)
#entries.each do |entry|
if (name == entry.name) && (email == entry.email) && (phone_number = entry.phone_number)
entry.delete #this line returns an error in my RSPEC test
else
p "Entry does not exist \n Please try again."
end
end
end
end
address_book_spec.rb
require_relative "../models/address_book"
RSpec.describe AddressBook do
let(:book) {AddressBook.new} # => lets us use the book variable in every test
describe "attributes" do
it "should respond to entries" do
# book = AddressBook.new # => Replaced by line 4
expect(book).to respond_to(:entries)
end
it "should initialize entries as an array" do
# book = AddressBook.new # => Replaced by line 4
expect(book.entries).to be_a(Array)
end
it "should initialize entries as an empty array" do
# book = AddressBook.new # => Replaced by line 4
expect(book.entries.size).to eq(0)
end
end
describe "#add_entry" do
it "adds only a single entry to the Address Book" do
# book = AddressBook.new # => Replaced by line 4
book.add_entry('Ada Lovelace', '010.012.1815', 'augusta.king#lovelace.com')
expect(book.entries.size).to eq(1)
end
it "adds the correct information to entries" do
# book = AddressBook.new # => Replaced by line 4
book.add_entry('Ada Lovelace', '010.012.1815', 'augusta.king#lovelace.com')
new_entry = book.entries[0]
expect(new_entry.name).to eq('Ada Lovelace')
expect(new_entry.phone_number).to eq('010.012.1815')
expect(new_entry.email).to eq('augusta.king#lovelace.com')
end
end
# added remove entry test
describe "#remove_entry" do
it "should remove a single entry" do
# book = AddressBook.new # => Replaced by line 4
book.add_entry('Austin Thesing', '800.445.8833','austin#thesing.xyz')
expect(book.entries.size).to eq(1)
book.remove_entry('Austin Thesing', '800.445.8833','austin#thesing.xyz')
expect(book.entries.size).to eq(0)
end
end
def check_entry(entry,expected_name,expected_phone_number, expected_email)
expect(entry.name).to eql(expected_name)
expect(entry.phone_number).to eql(expected_phone_number)
expect(entry.email).to eql(expected_email)
end
describe "#import_from_csv" do
it "import an entry from a CSV file" do
book.import_from_csv("entries.csv")
book_size = book.entries.size
expect(book_size).to eq 5 #checks the size of the book
end
it "adds the first entry" do
book.import_from_csv("entries.csv")
entry_one = book.entries[0]
check_entry(entry_one,"Mark Griffo","123456789","mark#bloc.com")
end
it "adds the second entry" do
book.import_from_csv("entries.csv")
entry_two = book.entries[1]
check_entry(entry_two,"Natalie Griffo","123456789","natalie#bloc.com")
end
it "adds the third entry" do
book.import_from_csv("entries.csv")
entry_three = book.entries[2]
check_entry(entry_three, "Steve Thesing", "8583878899", "steve#steve.com")
end
it "adds the fourth entry" do
book.import_from_csv("entries.csv")
entry_four = book.entries[3]
check_entry(entry_four, "Haidee Thesing", "8584458833", "h#thesing.com")
end
it "adds the fifth entry" do
book.import_from_csv("entries.csv")
entry_five = book.entries[4]
check_entry(entry_five, "Olivia Meers", "0987654321", "olivia#meers.com")
end
end
end
Terminal Output/Spec Failures
Austins-MacBook-Pro:address-bloc austinthesing$ rspec spec/address_book_spec.rb
.....F.FFFFF
Failures:
1) AddressBook#remove_entry should remove a single entry
Failure/Error: book.remove_entry('Austin Thesing', '800.445.8833','austin#thesing.xyz')
NoMethodError:
undefined method `delete' for #<Entry:0x007f8e8c1dea08>
# ./models/address_book.rb:37:in `block in remove_entry'
# ./models/address_book.rb:35:in `each'
# ./models/address_book.rb:35:in `remove_entry'
# ./spec/address_book_spec.rb:45:in `block (3 levels) in <top (required)>'
2) AddressBook#import_from_csv adds the first entry
Failure/Error: expect(entry.name).to eql(expected_name)
expected: "Mark Griffo"
got: "Haidee Thesing"
(compared using eql?)
# ./spec/address_book_spec.rb:50:in `check_entry'
# ./spec/address_book_spec.rb:64:in `block (3 levels) in <top (required)>'
3) AddressBook#import_from_csv adds the second entry
Failure/Error: expect(entry.name).to eql(expected_name)
expected: "Natalie Griffo"
got: "Mark Griffo"
(compared using eql?)
# ./spec/address_book_spec.rb:50:in `check_entry'
# ./spec/address_book_spec.rb:69:in `block (3 levels) in <top (required)>'
4) AddressBook#import_from_csv adds the third entry
Failure/Error: expect(entry.name).to eql(expected_name)
expected: "Steve Thesing"
got: "Natalie Griffo"
(compared using eql?)
# ./spec/address_book_spec.rb:50:in `check_entry'
# ./spec/address_book_spec.rb:74:in `block (3 levels) in <top (required)>'
5) AddressBook#import_from_csv adds the fourth entry
Failure/Error: expect(entry.name).to eql(expected_name)
expected: "Haidee Thesing"
got: "Olivia Meers"
(compared using eql?)
# ./spec/address_book_spec.rb:50:in `check_entry'
# ./spec/address_book_spec.rb:79:in `block (3 levels) in <top (required)>'
6) AddressBook#import_from_csv adds the fifth entry
Failure/Error: expect(entry.name).to eql(expected_name)
expected: "Olivia Meers"
got: "Steve Thesing"
(compared using eql?)
# ./spec/address_book_spec.rb:50:in `check_entry'
# ./spec/address_book_spec.rb:84:in `block (3 levels) in <top (required)>'
Finished in 0.0176 seconds (files took 0.08714 seconds to load)
12 examples, 6 failures
Failed examples:
rspec ./spec/address_book_spec.rb:40 # AddressBook#remove_entry should remove a single entry
rspec ./spec/address_book_spec.rb:61 # AddressBook#import_from_csv adds the first entry
rspec ./spec/address_book_spec.rb:66 # AddressBook#import_from_csv adds the second entry
rspec ./spec/address_book_spec.rb:71 # AddressBook#import_from_csv adds the third entry
rspec ./spec/address_book_spec.rb:76 # AddressBook#import_from_csv adds the fourth entry
rspec ./spec/address_book_spec.rb:81 # AddressBook#import_from_csv adds the fifth entry
First test is failing because the delete method is being used incorrectly:
entry.delete #this line returns an error in my RSPEC test
needs to be
#entries.delete(entry)
Entries are being inserted in a scrambled fashion because you are using break where you probably want
next
(I suppose that loop is supposed to insert in alphabetical order.)
break command terminates the full each block, so no more iterations are executed if there is one name of higher alphabetical order, next just skips to the next iteration.
That should be it

Custom assert message for multiple expect statements within match

I have written a custom match method in Rspec that matches an object against a hash. What I am trying to do is set custom failure messages for each line of the expect.
describe "/cars" do
car = FactoryGirl.create(:car, name: 'Alpha')
describe car do
it "displays single items" do
get cars_path
parsed_response = JSON.parse(response.body)
record_hash = parsed_response['cars'][0]
is_expected.to be_a_car_match_of(record_hash)
end
end
end
RSpec::Matchers.define :be_a_car_match_of do |hash|
match do |car|
expect(car.id).to eq(hash['id'])
expect(car.name).to eq(hash['name'])
end
failure_message do |car|
"expected that #{car} would be a match of #{hash}"
end
end
So what I would like is to have something like the following:
RSpec::Matchers.define :be_a_car_match_of do |hash|
match do |car|
expect(car.id).to eq(hash['id']) 'ids did not match'
expect(car.name).to eq(hash['name']) 'names did not match'
end
end
This would print out a much clearer error message.
I was initially doing this in mini-test but for a variety of reasons (outside of my control) needed to change it to rspec. The code I had in mini-test was:
def assert_car(car, hash)
assert_equal car.id, hash['id'], "ids did not match"
assert_equal car.name, hash['name'], "names did not match"
end
This is what I am trying to replicate.
Here is another example that requires less setup:
require 'rspec/expectations'
RSpec::Matchers.define :be_testing do |expected|
match do |actual|
expect(5).to eq(5)
expect(4).to eq(5)
end
failure_message do
"FAIL"
end
end
describe 'something' do
it 'something else' do
expect("expected").to be_testing('actual')
end
end
When this example is run, "FAIL" is printed out. On the other hand if I had:
describe 'something' do
it 'something else' do
expect(4).to eq(5)
end
end
I would get the following error message:
expected: 5
got: 4
This is what I want. I want to know what part of the custom matcher failed.
You could call the low-level matches? method instead of expect. Something like this:
require 'rspec/expectations'
RSpec::Matchers.define :be_a_positive_integer do
m1, m2 = nil, nil # matchers
r1, r2 = false, false # results
match do |actual|
m1 = be_a Integer # this returns matcher instances
m2 = be > 0
r1 = m1.matches?(actual) # evaluate matchers
r2 = m2.matches?(actual)
r1 && r2 # true if both are true
end
failure_message do |actual| # collect error messages from matchers
messages = []
messages << m1.failure_message unless r1
messages << m2.failure_message unless r2
messages.join("\n")
end
end
describe -1 do
it { is_expected.to be_a_positive_integer }
end
describe 1.0 do
it { is_expected.to be_a_positive_integer }
end
describe -1.0 do
it { is_expected.to be_a_positive_integer }
end
Output:
Failures:
1) -1 should be a positive integer
Failure/Error: it { is_expected.to be_a_positive_integer }
expected: > 0
got: -1
# ./ruby_spec.rb:24:in `block (2 levels) in <top (required)>'
2) 1.0 should be a positive integer
Failure/Error: it { is_expected.to be_a_positive_integer }
expected 1.0 to be a kind of Integer
# ./ruby_spec.rb:28:in `block (2 levels) in <top (required)>'
3) -1.0 should be a positive integer
Failure/Error: it { is_expected.to be_a_positive_integer }
expected -1.0 to be a kind of Integer
expected: > 0
got: -1.0
# ./ruby_spec.rb:32:in `block (2 levels) in <top (required)
I think aggregate_failures is what you are looking for :
It wraps a set of expectations with a block. Within the block, expectation failures will not immediately abort like normal; instead, the failures will be aggregated into a single exception that is raised at the end of the block, allowing you to see all expectations that failed.
See : https://relishapp.com/rspec/rspec-expectations/docs/aggregating-failures

Resources