Ruby Minitest simple test raise error - wrong number of arguments error - ruby

I'm trying to learn the TDD approach, to do so I'm using pure Ruby app which is responsible for formatting UK phone number. In my class I want to test if the whitespaces and are removed from given phone number. Basically I'm trying to test the Ruby .delete(' ') method.
Tested module
# lib/formatter/phone_number/uk.rb
module Formatter
module PhoneNumber
module Uk
(...)
def self.format(number)
number.delete(' ')
end
end
end
end
The test
# test/lib/formater/phone_number/uk_test.rb
require 'minitest/autorun'
class UkTest < Minitest::Test
test 'remove whitespaces' do
invalid_number = '+44 12 12 12 12 '
Formatter::PhoneNumber::Uk.format(invalid_number)
assert_equal '+4412121212'
end
end
Which give me an error:
test/lib/formater/phone_number/uk_test.rb:6:in `test': wrong number of arguments (given 1, expected 2) (ArgumentError)
Aside from the fact that testing a method built in Ruby is not a good idea, what am I doing wrong?

You must define a test-method (the method name must start with test_).
Inside the test method you define your assertions. In your case, you compare the expected value with the result of your method.
class UkTest < Minitest::Test
def test_remove_whitespaces
invalid_number = '+44 12 12 12 12 '
assert_equal '+4412121212', Formatter::PhoneNumber::Uk.format(invalid_number)
end
end
Full test in one file:
module Formatter
module PhoneNumber
module Uk
def self.format(number)
number.delete(' ')
end
end
end
end
require 'minitest/autorun'
class UkTest < Minitest::Test
def test_remove_whitespaces
invalid_number = '+44 12 12 12 12 '
assert_equal '+4412121212', Formatter::PhoneNumber::Uk.format(invalid_number)
end
end
Same test with minitest/spec
require 'minitest/spec'
describe Formatter::PhoneNumber::Uk do
it 'removes spaces from numbers' do
invalid_number = '+44 12 12 12 12 '
_(Formatter::PhoneNumber::Uk.format(invalid_number)).must_equal('+4412121212')
end
end

Related

Ruby with RSpec NoMethodError: undefined method length for nil:NilClass

I'm new to Ruby and RSpec trying to write a unit case for string length. I have 3 rb files as follows
1. Calling file
require_relative 'ruby_final_operations'
require_relative 'ruby_helper'
require 'uri'
require 'open-uri'
require 'prime'
module RubyOperations
# Public: Various commands for the user to interact with RubyCommand.
class Command
res = RubyOperations::Operations.new
res.letter_count(res.inputstr)
2nd File - Method Implementation
require_relative 'ruby_helper'
require 'logger'
$FILE_LOG = RubyOperations.create_log(File.expand_path('~/RubyOperations_LOG.log'), Logger::DEBUG)
$STD_LOG = RubyOperations.create_log(nil, Logger::INFO)
module RubyOperations
class Operations
def inputstr
RubyOperations.log('Enter the String:[Length 20]',:BOTH)
#str = gets.chomp
raise StandardError if #str =~ /\d/ || #str.empty? || #str.length > 20
rescue StandardError,ArgumentError => e
RubyOperations.log(e,:ERROR)
end
def letter_count(str)
result = #str.length
RubyOperations.log("The number of letters in the string: #{result}",:BOTH)
end
3rd file - RSpec
require 'ruby_final_operations'
describe 'RubyOperations' do
describe 'Operations' do
subject = RubyOperations::Operations.new
describe '.letter_count' do
context 'when operation is provided' do
it 'returns letter count' do
allow(subject.letter_count("hello").to receive(:result).and_return(5)
end
end
end
The problem is that in the 2nd File he argument is 'str' but the typed string is stored is '#str'.
How can I pass the string 'hello' from the rspec file to test this.
There are a few issues:
Calling a instance_method with an argument that is not used
def letter_count #get rid of argument, the argument does nothing,
#basically it looks you added the argument,
# just, so you can call the other method there.
Make your main simple, with a clear sequence
res.inputstr
res.letter_count
But about your actual question, in your test you change the wrong thing the wrong method
allow(subject.letter_count("hello").to receive(:result).and_return(5)
# letter count should do the log entry, not return five, at least that what your method say
So you probably want to set the #str before you test the letter_count method.
subject.instance_variable_set("hello")
# then test for what you expect the method to return
expect(subject.letter_count).to eq(5)
# this specific test will fail, because you are doing a log entry, and not return the length on letter_count.

Writing a test for a case statement in Ruby

I'm trying to write a test for a case statement using minitest. Would I need to write separate tests for each "when"? I included my code below. Right now it just puts statements, but eventually it's going to redirect users to different methods. Thanks!
require 'pry'
require_relative 'messages'
class Game
attr_reader :user_answer
def initialize(user_answer = gets.chomp.downcase)
#user_answer = user_answer
end
def input
case user_answer
when "i"
puts "information"
when "q"
puts "quitter"
when "p"
puts "player play"
end
end
end
This answer will help you. Nonetheless I'll post one way of applying it to your situation. As suggested by #phortx when initializing a game, override the default user-input with the relevant string. Then by using assert_output we can do something like:
#test_game.rb
require './game.rb' #name and path of your game script
require 'minitest/autorun' #needed to run tests
class GameTest < MiniTest::Test
def setup
#game_i = Game.new("i") #overrides default user-input
#game_q = Game.new("q")
#game_p = Game.new("p")
end
def test_case_i
assert_output(/information\n/) {#game_i.input}
end
def test_case_q
assert_output(/quitter\n/) {#game_q.input}
end
def test_case_p
assert_output(/player play\n/) {#game_p.input}
end
end
Running the tests...
$ ruby test_game.rb
#Run options: --seed 55321
## Running:
#...
#Finished in 0.002367s, 1267.6099 runs/s, 2535.2197 assertions/s.
#3 runs, 6 assertions, 0 failures, 0 errors, 0 skips
You have to test each case branch. Via RSpec it would work that way:
describe Game do
subject { Game }
describe '#input' do
expect_any_instance_of(Game).to receive(:puts).with('information')
Game.new('i').input
expect_any_instance_of(Game).to receive(:puts).with('quitter')
Game.new('q').input
expect_any_instance_of(Game).to receive(:puts).with('player play')
Game.new('p').input
end
end
However due the fact that puts is ugly to test, you should refactor your code to something like that:
require 'pry'
require_relative 'messages'
class Game
attr_reader :user_answer
def initialize(user_answer = gets.chomp.downcase)
#user_answer = user_answer
end
def input
case user_answer
when "i"
"information"
when "q"
"quitter"
when "p"
"player play"
end
end
def print_input
puts input
end
end
Then you can test with RSpec via:
describe Game do
subject { Game }
describe '#print_input' do
expect_any_instance_of(Game).to receive(:puts).with('quitter')
Game.new('q').print_input
end
describe '#input' do
expect(Game.new('i').input).to eq('information')
expect(Game.new('q').input).to eq('quitter')
expect(Game.new('i').input).to eq('player play')
expect(Game.new('x').input).to eq(nil)
end
end

Can rspec's "expect" parse a block to confirm a nested array/grid?

How can the nested array be checked using rspecs expect syntax?
This code block works using rspecs should syntax:
subject.cell_grid.each do |row|
row.is_a?(Array).should be_true
end
...and I think I've got the syntax correct on lines 22 & 23 of the "spec_game_of_life.rb" file, but, when I check the file with rspec I get the following error:
user#ubuntu:~/Ruby/GameOfLife$ rspec spec_game_of_life.rb
..F
Failures:
1) Game of Life world should create proper cell_grid upon initialization
Failure/Error:
expect(subject.cell_grid.each) do |row|
row.is_a?(Array).to be true
end
ArgumentError:
You cannot pass both an argument and a block to `expect`.
# ./spec_game_of_life.rb:22:in `block (3 levels) in <top (required)>'
Finished in 0.0019 seconds (files took 0.11777 seconds to load)
3 examples, 1 failure
Failed examples:
rspec ./spec_game_of_life.rb:19 # Game of Life world should create proper cell_grid upon initialization
I understand that rspec's "should" has been replaced with "expect" per: http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
Initializing a class with a (rows, cols) cell grid - Ruby script, "game_of_life.rb":
1 # basic file
2
3 class World
4 attr_accessor :rows, :cols, :cell_grid
5 def initialize(rows=3, cols=3)
6 #rows = rows
7 #cols = cols
8 #cell_grid = Array.new(rows) do |row|
9 Array.new(cols) do |col|
10 end
11 end
12 end
13 end
Ruby spec file, "spec_game_of_life.rb":
1 # spec file
2
3 require 'rspec'
4 require_relative 'game_of_life.rb'
5
6 describe 'Game of Life' do
7
8 context 'world' do
9 subject { World.new }
10
11 it 'should create a new world object' do
12 expect(subject.is_a?(World)).to be true
13 end
14 it 'should respond to proper methods' do
15 expect(subject.respond_to?(:rows))
16 expect(subject.respond_to?(:cols))
17 expect(subject.respond_to?(:cell_grid))
18 end
19 it 'should create proper cell_grid upon initialization' do
20 expect(subject.cell_grid.is_a?(Array)).to be true
21
22 expect(subject.cell_grid.each) do |row|
23 row.is_a?(Array).to be true
24 end
25 end
26
27 end
28
29 end
FWIW: Ubuntu 14.04, Ruby 2.3.0, rspec 3.5.3 & I'm following along with this "Game of Life" tutorial which uses "should":
https://www.youtube.com/watch?v=Tzs3_pl410M&list=PLMC91Ry9EhRKUn0MIdgXrZiptF7nVyYoQ&index=4
EDIT per answer from Bartek Gladys:
expect{ subject.cell_grid.all? { |k| k.is_a?(Array) } }.to eq true
..F
Failures:
1) Game of Life world should create proper cell_grid upon initialization
Failure/Error: expect{ subject.cell_grid.all? { |k| k.is_a?(Array) } }.to eq true
You must pass an argument rather than a block to use the provided matcher (eq true), or the matcher must implement `supports_block_expectations?`.
# ./spec_game_of_life.rb:22:in `block (3 levels) in <top (required)>'
NOTE:
expect{ subject.cell_grid.all? { |k| k.is_a?(Array) } }.to be true
Failure/Error: expect{ subject.cell_grid.all? { |k| k.is_a?(Array) } }.to be true
You must pass an argument rather than a block to use the provided matcher (equal true), or the matcher must implement `supports_block_expectations?`.
So... how to implement supports_block_expectations??
Researching per: http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/
https://www.relishapp.com/rspec/rspec-expectations/docs/custom-matchers/define-a-matcher-supporting-block-expectations
http://www.relishapp.com/rspec/rspec-expectations/v/3-5/docs
http://rspec.info/
The expect version of your spec is
subject.cell_grid.each do |row|
expect(row.is_a?(Array)).to be_truthy
end
(The be_true matcher no longer exists)
Slightly more naturally you would write
subject.cell_grid.each do |row|
expect(row).to be_an(Array)
end
You could use all? to have only one call to expect but this will lead to less helpful failure messages.
You wouldn't usually pass a block to expect just to make an assertion about a value - typically this is used to check for side effects (such as raising an exception).
try this:
expect{ subject.cell_grid.all? { |k| k.is_a?(Array) } }.to eq true
Not exactly the answer, but as I struggled to find how to define the supports_block_expectations thing, it's below:
Say, we are to give block support to matcher be (can be anything like eq, or equal or even your own matcher thing)
CAUTION: Using existing matchers kinda redefine it. Better name it with something new to avoid trouble.
Definition:
Short hand definition:
RSpec::Matchers.define :be do
match do |actual|
actual.is_a? Proc
end
supports_block_expectations
end
Full definition
RSpec::Matchers.define :equal do
match do |actual|
actual.is_a? Proc
end
def supports_block_expectations?
true # or some logic
end
end
Usage:
RSpec.describe "a custom block matcher" do
it { expect { subject.name }.to be('abc') }
end

Rubywarrior Level 4 (cleaning up my code help)

I'm learning programming through Ruby and I found the awesome Rubywarrior by Ryan Bates from Railscasts. Unfortunately I'm stuck in that my code is throwing up syntax error messages (unexpected $end).
I'm not asking for the answer, I would like to sort that out myself, but if someone could point out where my code is getting the error from that would be super. Thanks!
class Player
def initialize
#maxhealth = 20
#dying = 7
#previoushealth = #maxhealth
#health = warrior.health
#warrior = warrior
end
def play_turn(warrior)
# If there are no enemies, rest until health is 100%
turn_start_check(warrior)
actions(warrior)
turn_end_check(warrior)
end
def actions(warrior)
if #damaged_since_last_turn
warrior.shoot!
elsif
#health < #maxhealth
warrior.rest!
else
warrior.walk!
end
end
def hurt?(warrior)
warrior.health < 20
end
def healthy?(warrior)
warrior.health = 20
end
def alone?(warrior)
warrior.feel.empty?
end
def should_i_move?(warrior)
if healthy? and alone?
warrior.rest!
else
warrior.walk!
end
# Put code here for if health from previous turn is less than last term
# If true don't rest and keep moving forward
def turn_start_check(warrior)
#damaged_since_last_turn = #previoushealth > warrior.health
end
def turn_end_check(warrior)
#previoushealth = warrior.health
end
end
My guess:
def should_i_move?(warrior)
if healthy? and alone?
warrior.rest!
else
warrior.walk!
end # <<MISSING THIS ONE
end
That error message means that you are missing an end keyword somewhere. Check your code to see if all your statements are properly written.
With Ruby 1.9.3, if you turn on warnings, you get the following warnings:
-:46: warning: mismatched indentations at 'end' with 'if' at 42
-:57: warning: mismatched indentations at 'end' with 'def' at 41
and then the error
-:57: syntax error, unexpected $end, expecting keyword_end
line 46 corresponds to the first end after def should_i_move?(warrior)
This should work with earlier versions of Ruby 1.9 as well.

Are there any good ruby testing traceability solutions?

I'm writing some ruby (not Rails) and using test/unit with shoulda to write tests.
Are there any gems that'll allow me to implement traceability from my tests back to designs/requirements?
i.e.: I want to tag my tests with the name of the requirements they test, and then generate reports of requirements that aren't tested or have failing tests, etc.
Hopefully that's not too enterprisey for ruby.
Thanks!
Update: This solution is available as a gem: http://rubygems.org/gems/test4requirements
Are there any gems that'll allow me to implement traceability from my
tests back to designs/requirements?
I don't know any gem, but your need was inspiration for a little experiment, how it could be solved.
You have to define your Requirements with RequirementList.new(1,2,3,4)
This requirements can be assigned with requirements in a TestCase
each test can be assigned to a requirement with requirement
after the test results you get an overview which requirements are tested (successfull)
And now the example:
gem 'test-unit'
require 'test/unit'
###########
# This should be a gem
###########
class Test::Unit::TestCase
def self.requirements(req)
##requirements = req
end
def requirement(req)
raise RuntimeError, "No requirements defined for #{self}" unless defined? ##requirements
caller.first =~ /:\d+:in `(.*)'/
##requirements.add_test(req, "#{self.class}##{$1}")
end
alias :run_test_old :run_test
def run_test
run_test_old
#this code is left if a problem occured.
#in other words: if we reach this place, then the test was sucesfull
if defined? ##requirements
##requirements.test_successfull("#{self.class}##{#method_name}")
end
end
end
class RequirementList
def initialize( *reqs )
#requirements = reqs
#tests = {}
#success = {}
#Yes, we need two at_exit.
#tests are done also at_exit. With double at_exit, we are after that.
#Maybe better to be added later.
at_exit {
at_exit do
self.overview
end
}
end
def add_test(key, loc)
#fixme check duplicates
#tests[key] = loc
end
def test_successfull(loc)
#fixme check duplicates
#success[loc] = true
end
def overview()
puts "Requirements overiew"
#requirements.each{|req|
if #tests[req] #test defined
if #success[#tests[req]]
puts "Requirement #{req} was tested in #{#tests[req] }"
else
puts "Requirement #{req} was unsuccessfull tested in #{#tests[req] }"
end
else
puts "Requirement #{req} was not tested"
end
}
end
end #RequirementList
###############
## Here the gem end. The test will come.
###############
$req = RequirementList.new(1,2,3, 4)
class MyTest < Test::Unit::TestCase
#Following requirements exist, and must be tested sucessfull
requirements $req
def test_1()
requirement(1) #this test is testing requirement 1
assert_equal(2,1+1)
end
def test_2()
requirement(2)
assert_equal(3,1+1)
end
def test_3()
#no assignment to requirement 3
pend 'pend'
end
end
class MyTest_4 < Test::Unit::TestCase
#Following requirements exist, and must be tested sucessfull
requirements $req
def test_4()
requirement(4) #this test is testing requirement 4
assert_equal(2,1+1)
end
end
the result:
Loaded suite testing_traceability_solutions
Started
.FP.
1) Failure:
test_2(MyTest)
[testing_traceability_solutions.rb:89:in `test_2'
testing_traceability_solutions.rb:24:in `run_test']:
<3> expected but was
<2>.
2) Pending: pend
test_3(MyTest)
testing_traceability_solutions.rb:92:in `test_3'
testing_traceability_solutions.rb:24:in `run_test'
Finished in 0.65625 seconds.
4 tests, 3 assertions, 1 failures, 0 errors, 1 pendings, 0 omissions, 0 notifications
50% passed
Requirements overview:
Requirement 1 was tested in MyTest#test_1
Requirement 2 was unsuccessfull tested in MyTest#test_2
Requirement 3 was not tested
Requirement 4 was tested in MyTest_4#test_4
If you think, this could be a solution for you, please give me a feedback. Then I will try to build a gem out of it.
Code example for usage with shoulda
#~ require 'test4requirements' ###does not exist/use code above
require 'shoulda'
#use another interface ##not implemented###
#~ $req = Requirement.new_from_file('requirments.txt')
class MyTest_shoulda < Test::Unit::TestCase
#Following requirements exist, and must be tested sucessfull
#~ requirements $req
context 'req. of customer X' do
#Add requirement as parameter of should
# does not work yet
should 'fullfill request 1', requirement: 1 do
assert_equal(2,1+1)
end
#add requirement via requirement command
#works already
should 'fullfill request 1' do
requirement(1) #this test is testing requirement 1
assert_equal(2,1+1)
end
end #context
end #MyTest_shoulda
With cucumber you can have your requirement be the test, doesn't get any more traceable than that :)
So a single requirement is a feature, and a feature has scenario that you want to test.
# addition.feature
Feature: Addition
In order to avoid silly mistakes
As a math idiot
I want to be told the sum of two numbers
Scenario Outline: Add two numbers
Given I have entered <input_1> into the calculator
And I have entered <input_2> into the calculator
When I press <button>
Then the result should be <output> on the screen
Examples:
| input_1 | input_2 | button | output |
| 20 | 30 | add | 50 |
| 2 | 5 | add | 7 |
| 0 | 40 | add | 40 |
Then you have step definitions written in ruby mapped to the scenario
# step_definitons/calculator_steps.rb
begin require 'rspec/expectations'; rescue LoadError; require 'spec/expectations'; end
require 'cucumber/formatter/unicode'
$:.unshift(File.dirname(__FILE__) + '/../../lib')
require 'calculator'
Before do
#calc = Calculator.new
end
After do
end
Given /I have entered (\d+) into the calculator/ do |n|
#calc.push n.to_i
end
When /I press (\w+)/ do |op|
#result = #calc.send op
end
Then /the result should be (.*) on the screen/ do |result|
#result.should == result.to_f
end

Resources