Rspec test for a Die's number of sides failing - ruby

Heys guys i am beginner in rspec Can anybody help me with this problem.
and my test returns the number of sides that the die possesses fail,what i am doing wrong??
require 'rspec'
require_relative 'die'
describe Die do
describe '#initialize' do
it 'expects a single argument' do
expect(Die.instance_method(:initialize).arity).to eq 1
end
it 'raises ArgumentError if sides are < 1' do
expect {
Die.new(-1)
}.to raise_error(ArgumentError)
expect {
Die.new(0)
}.to raise_error(ArgumentError)
end
end
describe 'Instance method' do
before(:each) do
#sides = rand(50)
#die = Die.new(#sides)
end
context '#num_of_sides' do
it 'expects method to have no arguments' do
expect(Die.instance_method(:num_of_sides).arity).to eq 0
end
it 'returns the number of sides that the die possesses' do
expect(#die.num_of_sides).to eq #sides
end
end
context "#roll" do
it 'expects roll method to have no arguments' do
expect(Die.instance_method(:roll).arity).to eq 0
end
it "returns a random number between 1 and number_of_sides" do
rolls = Array.new(10000) {#die.roll}.uniq.sort
possible_values = (1..#sides).to_a
expect(rolls).to eq possible_values
end
end
end
end
Ruby Die Class:
class Die
def initialize(sides)
raise ArgumentError if sides < 1
end
def num_of_sides(sides=nil)
if sides!=nil
Random.rand(50)
else
0
end
end
def roll()
end
end
Output: 6 examples, 2 failures, 4 passed

You have not yet implemented the Die class so that it remembers the number of sides it was instantiated with.
To do this, you would normally use an instance variable e.g. #num_of_sides, and set it equal to the sides param in the constructor, after you have checked for a valid input.
To read the number of sides you can either just return the value of #num_of_sides (alter your existing method to be much simpler, it doesn't need to take any parameters, and only needs to return the value you have stored, no tests/logic or randomness required), or you can use the short-cut attr_reader :num_of_sides which creates that method for you.
It would be worth reviewing any notes you have on Ruby instance variables, as this seems to be something you still need to get to grips with. Or you could take a look at this Ruby Monk Primer on creating classes.

Related

Rspec for checking of a number

I am a ruby newbie, I have managed to pull out the code for ruby but writing rspecs for them seems problematic. It's hard to understand the way to write rspecs even after reading few tutorials. Someone please help me to write for an input method then I would try to refactor it for the rest.
RB file:
module RubyOperations
class Operations
def input(num)
RubyOperations.log('Enter a number:[Between 1 to 10]',:BOTH)
num = Integer(gets.chomp)
raise StandardError if num <= 0 || num > 10
return num
rescue StandardError, ArgumentError => e
RubyOperations.log(e,:ERROR)
end
end
end
RSPEC:
describe 'RubyOperations' do
describe 'Operations' do
describe '.input' do
context 'when number is provided' do
it 'returns the number provided' do
expect(RubyOperations.input(num)).to eq(Integer)
end
end
end
end
end
You can check the class of the output of the method to equal integer
require 'ruby_final_operations'
describe 'RubyOperations' do
describe 'Operations' do
describe '.input' do
context 'when number is provided' do
it 'returns the number provided' do
expect(RubyOperations.input(num).class).to eq(Integer)
(or)
expect(RubyOperations.input(num)).to be_a_kind_of(Integer)
end
end
end
end
end
And whenever you write rspec keep in mind
If the method for which you are writing rspec deals with manipulations in your db then check if db is manipulated or not
Or if you are writing rspec for any methods which returns an object then procced like this
if a method is defined like
def square_of_a_number(num)
num*num
end
Then write rspec like this
it 'returns square of a number' do
expect(square_of_a_number(2).to eq(4)
end
For any methods that you know the output of a method will be that then hardcode the input or user Faker gem for input of the method expect the expected result of that method
There are few issues with code that you have shared:
1) In the Operations class, the method input receives an argument which is not used anywhere because of this line: num = Integer(gets.chomp). Basically gets is the method that waits for user input, and the assignment num = ... overrides the value of argument (num) that is passed into the method, hence it is pointless to pass num argument into the method.
2) In your spec sample you call input method on RubyOperations module, while the input lives in class Operations under namespace RubyOperations. Also method input is not a class method but instance method. So proper method call would be: RubyOperations::Operations.new.input(5)
3) To run a spec for input method you would need to stub user input. RSpec-mocks gem can help you with that - https://github.com/rspec/rspec-mocks. It has allow stub method: allow(object).to receive(:gets) { "5" }
The whole sample will be:
it 'returns the number provided' do
# instantiate object that we will test
subject = RubyOperations::Operations.new
# we stub method 'gets' and whenever it is called we return string "5"
allow(subject).to receive(:gets) { "5" }
# we call method input with argument 1, the argument 1 is pointless as described in point 1) and can be omitted
expect(subject.input(1)).to eq(5)
end

Ruby wrong number of arguments in class instantitation

I am trying to make a battleship game in Ruby, but when I try to create an instance of the game board, I get "wrong number of arguments, 0 for 1". I dont see where I am going wrong as the initialize definition clearly accepts arguments.
class Board
attr_reader :grid, :default_grid
def intitalize(grid = self.class.default_grid, random = false)
#grid = grid
make_random_board if random
end
def self.default_grid
grid = Array.new(10){Array.new(10)}
end
def count
grid.flatten.count{|x| x == :s}
end
def self.random
self.new(self.default_grid, true)
end
def empty?(position = nil)
return true if position.nil?
else
false
end
def full?
grid.flatten.none?(&:nil?)
end
def place_random_ship
if full?
raise "error, board is full"
end
space = [rand(grid.length),rand(grid.length)]
until empty?(space)
space = [rand(grid.length),rand(grid.length)]
end
self[space] = :s
end
def make_random_board(count = 10)
count.times do
place_random_ship
end
end
end
emptygrid = Array.new(2){Array.new(2)}
myGame = Board.new(emptygrid)
You have a typo in your code. You should be using initialize instead of intitalize
And i believe the error you might have been getting would be ArgumentError: wrong number of arguments (1 for 0)
Which is because of your typo, the default class initialize method was used, which doesn't take in any arguments.
And something unrelated that i noticed in your code. You have defined a method named count and use variables named count. This is a code smell and i would suggest naming the method differently, because down the line, this might cause some bugs, that you might find hard to debug.

Unexpected rspec behavior

Learning Rspec, working with just Ruby, not Rails. I have a script that works as expected from the command line, but I can't get the test to pass.
The relevant code:
class Tree
attr_accessor :height, :age, :apples, :alive
def initialize
#height = 2
#age = 0
#apples = false
#alive = true
end
def age!
#age += 1
end
And the spec:
describe "Tree" do
before :each do
#tree = Tree.new
end
describe "#age!" do
it "ages the tree object one year per call" do
10.times { #tree.age! }
expect(#age).to eq(10)
end
end
end
And the error:
1) Tree #age! ages the tree object one year per call
Failure/Error: expect(#age).to eq(10)
expected: 10
got: nil
(compared using ==)
I think that is all the relevant code, please let me know if I'm missing something in the code I posted. From what I can tell the error comes from scoping within rspec, and the #age variable is not being passed into the rspec test in a way that I think it should, thus being nil when trying to call the function within the test.
#age is a variable within each of your Tree objects. You're right that this is a scoping 'problem', more a scoping feature - your test has no variable named #age.
What it does have is a variable called #tree. That Tree has a property called age. This should work, let me know if it doesn't:
describe "Tree" do
before :each do
#tree = Tree.new
end
describe "#age!" do
it "ages the tree object one year per call" do
10.times { #tree.age! }
expect(#tree.age).to eq(10) # <-- Change #age to #tree.age
end
end
end

Trouble getting tests to run properly

I've been trying to test a program that simulates an elevator for two days now with little success. Here's my elevator class, the program is still a work in progress and I've also commented out some methods that might not be essential to the test I'm having trouble with. I'll gladly show more code if you think it's needed
class Elevator
attr_accessor :current_floor
GROUND = 0
TOP = 15
def initialize
#floors = [] # list of floors to travel to
#pending = [] # store floors not in direction of travel
#current_floor = GROUND
#going_up = true # cannot travel downward from ground floor
#going_down = false
end
def get_input
gets.chomp
end
def run
enter_floors
sort_floors
move_to_floor
end
def enter_floors
# prompts the user for input and calls check_floor_numbers
end
def check_floor_numbers floors
# calls validate_floors to ensure user entered '2' instead of 'two'
# if proper floor numbers have been entered this method adds the number
# to #floors array other wise it calls floor_error_message
end
def floor_error_message
puts "Please enter numbers only."
enter_floors
end
def sort_floors
# if we are on the ground floor this method sorts #floors in ascending order
# if we are on the top floor it sorts #floors in descending order
# else it calls check_direction_of_travel
end
def move_to_floor
floor = #floors[0]
if #current_floor == floor
puts "You are already on floor #{floor}"
else
print_direction
(#current_floor..floor).each { |floor| puts "...#{floor}" }
#current_floor = floor # update current_floor
#floors.delete_at(0) # remove floor from list
end
check_for_more_passengers
end
def check_for_more_passengers
puts "Are there any more passengers? (Y/N)"
answer = (get_input).upcase
answer == 'Y' ? run : check_next_move
end
def check_next_move
if #floors.empty? && #pending.empty?
end_ride
else
move_to_floor
end
end
def check_direction_of_travel
# not implemented - add floor to appropriate array depending on dir
# of travel
end
def end_ride
puts "\n\nEND."
end
def print_direction
msg = " "
#going_up ? msg = "Going Up!" : msg = "Going Down!"
puts msg
end
end
I'm trying to test that the elevator can move to a specific floor. At first I was having trouble testing input from the console without running the program itself. I asked a question about this and was referred to this answer in a different question. The answer in question extract gets.chomp to a separate method then overrides the method in the tests. I ended up with something like this:
describe "it can move to a floor" do
before do
##moves = ["2", "N"]
def get_input; ##moves.next end
end
it "should move to floor 2" do
e = Elevator.new
e.run
assert_equal(e.current_floor, 2)
end
end
Problem: get_input was not properly overidden and running the test suit prompted the user for input so it was suggested that I open the Elevator class in the test itself to ensure that the method was properly overridden. Attempting to do so eventually led to a test like this:
describe "it can move to a floor" do
before do
class Elevator
attr_accessor :current_floor
##moves = ["2", "N"]
def get_input; ##moves.next end
def run; end
end
end
it "should move to floor 2" do
e = Elevator.new
e.run
assert_equal(e.current_floor, 2)
end
end
I had to override run and add an attr_accessor for current_floor because I was getting method missing errors.
Problem: This test gives the following error:
1) Failure: it can move to a floor#test_0001_should move to floor 2
[elevator_test.rb:24]: Expected: nil Actual: 2
I've tried to tidy up the Elevator class as much as possible and keep the methods as simple as I could given the parameters of the program.
Can anyone point me in the right direction towards getting this solved, with maybe pseudocode examples (if possible) to demonstrate how I should approach this problem if the answer is to refactor.
Please bear in mind that I'd also like to implement other tests like checking that the elevator class can maintain a list of floors, or that it can change direction, in the future when you answer.
Your test class ElevatorTest is redefining the Elevator to override method get_input, but it is not opening the class Elevator defined in elevator.rb, but instead it is sort of creating a new class Elevator which happens to be defined inside the class ElevatorTest. Remember every class is also a module, so now you have a new class ElevatorTest::Elevator.
To fix this issue, I have made some changes to elevator_test.rb which is shown below.
gem 'minitest', '>= 5.0.0'
require 'minitest/spec'
require 'minitest/autorun'
require_relative 'elevator'
class Elevator
##moves = ["2", "N"].each
def get_input; ##moves.next end
end
class ElevatorTest < MiniTest::Test
def test_working
assert_equal(1, 1)
end
describe "it can move to a floor" do
before do
end
it "should move to floor 2" do
e = Elevator.new
e.run
assert_equal(e.current_floor, 2)
end
end
end
Also, please remember to use .each while defining ##moves - it returns an enumerator. We can call .next only on an enumerator

How would this spec pass

I ran into a github spec that was failing and as I am learning how to write specs, I fixed two of them that were failing apart for the last one in with a comment #THIS ONE IS STILL FAILING. How would one make it pass?
class Team
attr_reader :players
def initialize
#players = Players.new
end
end
class Players
def initialize
#players = ["","Some Player",""]
end
def size
#players.size
end
def include? player
raise "player must be a string" unless player.is_a?(String)
#players.include? player
end
end
describe "A new team" do
before(:each) do
#team = Team.new
end
it "should have 3 players (failing example)" do
#team.should have(3).players
end
it "should include some player (failing example)" do
#team.players.should include("Some Player")
end
#THIS ONE IS STILL FAILING
it "should include 5 (failing example)" do
#team.players.should include(5)
end
it "should have no players"
end
I'll assume that the aim is to modify the spec, not to modify the code to pass the spec.
In this case, we don't actually expect #team.players to include 5; rather, we expect it to raise an exception when asked whether it includes a non-string. This can be written as follows:
it "should raise an exception when asked whether it includes an integer" do
expect {
#team.players.should include(5)
}.to raise_exception
end
For a check on the exception type and message, use raise_exception(RuntimeError, "player must be a string").
You should probably modify the descriptions of the other two specs similarly, since they no longer fail.

Resources