Is it possible to write a spec for infinite-loop issues using Rspec?, Ruby - ruby

Listen I've an interesting question here, the other day I ran into an "infinite-loop" problem using Rspec, Rspec couldn't even go through the spec related to other methods inside the loop and even the comp was almost crashing. Very funny.
I'd like to test my future loops (While-loop in this case) against infinite loop-code.
How I can test this while-loop and catch up this problem like this one and make the proper correction?
Thanks!
This is my code from other day:
i = 0
while i <= Video.all.count do
if ( #sampler = Video.find_next_sampler(#samplers[-1].end_time, #samplers[-1].end_point) )
#samplers << #sampler
else
flash[:error] = 'There is not any more match for this video-sampler'
end
i + 1 #Now Here is the bug!! IT should be: i += 1
end

require 'timeout'
it 'should not take too long' do
Timeout.timeout(20) do
... blah ...
end
end
Or even
# spec_helper.rb
require 'timeout'
RSpec.configure do |c|
c.around(:example, finite: true) do |example|
Timeout.timeout(20) do
example.run
end
end
end
# my_spec.rb
it "should work really fast", finite: true do
... blah ...
end

In this particular example is doesn't make sense to run the loop more often that the total number of all videos in the database.
Therefore I would try something like this:
let(:videos_count) { Video.count }
before do
allow(Video).to receive(:find_next_sampler).and_call_original
end
it 'is not an infinite loop' do
except(Video).to receive(:find_next_sampler).at_most(videos_count).times
# call your method
end

Related

Using Rspec in one file like test/unit

When I have time I like to take on a challenge at codewars.
Until now I used test/unit to do my unit testing but I would like to use Rspec now without changing my way of working. These are small methods/files/tests so I like to keep everything together in one script.
I run almost all of my code using Sublime Text and get the result in a window at the bottom of the editor.
Here my working test/unit example
require 'test/unit'
def anagrams(word, words)
words.select { |w| w.chars.sort == word.chars.sort }
end
class MyTest < Test::Unit::TestCase
def test_fail
assert_equal(['aabb', 'bbaa'], anagrams('abba', ['aabb', 'abcd', 'bbaa', 'dada']) )
assert_equal(['carer', 'racer'], anagrams('racer', ['crazer', 'carer', 'racar', 'caers', 'racer']) )
assert_equal([], anagrams('laser', ['lazing', 'lazy', 'lacer']) )
end
end
This gives in Sublime the following output
Loaded suite C:/Users/.../codewars/anagram
Started
.
Finished in 0.001 seconds.
------
1 tests, 3 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
------
1000.00 tests/s, 3000.00 assertions/s
[Finished in 0.3s]
And here what I tried for Rspec
require 'rspec'
describe "Anagrams" do
def anagrams(word, words)
words.select { |w| w.chars.sort == word.chars.sort }
end
it "should only match the anagrams" do
anagrams('abba', ['aabb', 'abcd', 'bbaa', 'dada']) == ['aabb', 'bbaa']
end
end
In Sublime text, I get no output, just a empty black window with the time it took to execute the script, if I use the console and run rspec anagram.rb I get
.
Finished in 0.001 seconds (files took 0.10601 seconds to load)
1 example, 0 failures
How do I have my code and test in the same file and do the test just by running the script in my Sublime Text editor (and get the output) and how could I better rephrase this test ?
You just need to tell the RSpec::Core::Runner to run your spec.
Adding RSpec::Core::Runner.run([$__FILE__]) at the end of your fill should work.
Updated code:
require 'rspec'
describe "Anagrams" do
def anagrams(word, words)
words.select { |w| w.chars.sort == word.chars.sort }
end
it "should only match the anagrams" do
anagrams('abba', ['aabb', 'abcd', 'bbaa', 'dada']) == ['aabb', 'bbaa']
end
end
RSpec::Core::Runner.run([$__FILE__])
I don't know what magic you used for sublime text to auto run your unit tests.
What I can answer is how to phrase the test better.
require 'rspec'
def anagrams(word, words)
words.select { |w| w.chars.sort == word.chars.sort }
end
RSpec.describe "Anagrams" do
it "matches words that are anagrams" do
# 2 different ways to do this
expect(anagrams('abba', ['aabb', 'abcd', 'bbaa', 'dada'])).to match_array(['aabb', 'bbaa'])
expect(anagrams('abba', ['aabb', 'abcd', 'bbaa', 'dada'])).to contain_exactly('aabb', 'bbaa')
end
end
match_array & contain_exactly are identical, except that match_array needs 1 parameter: array and contain exactly needs no array, instead you list all memebers of array.
https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/contain-exactly-matcher
You could also break this into 2 or more specs if you want to. I'd do that if logic was more complicated. Going to do it here anyway so you can see more examples of rspec messages. Using should in spec name is no longer recommended.
RSpec.describe "Anagrams" do
it "when no annagrams found returns empty array" do
expect(anagrams('abba', ['abcd', 'dada'])).to eq([])
end
it "recognizes itself as annagram" do
expect(anagrams('abba', ['abba'])).to eq(['abba'])
end
it "returns array containing words that are anagrams" do
expect(anagrams('abba', ['aabb', 'abcd', 'bbaa', 'dada'])).to contain_exactly('aabb', 'bbaa')
end
end

Ruby Parallel each loop

I have a the following code:
FTP ... do |ftp|
files.each do |file|
...
ftp.put(file)
sleep 1
end
end
I'd like to run the each file in a separate thread or some parallel way. What's the correct way to do this? Would this be right?
Here's my try on the parallel gem
FTP ... do |ftp|
Parallel.map(files) do |file|
...
ftp.put(file)
sleep 1
end
end
The issue with parallel is puts/outputs can occur at the same time like so:
as = [1,2,3,4,5,6,7,8]
results = Parallel.map(as) do |a|
puts a
end
How can I force puts to occur like they normally would line separated.
The whole point of parallelization is to run at the same time. But if there's some part of the process that you'd like to run some of the code sequentially you could use a mutex like:
semaphore = Mutex.new
as = [1,2,3,4,5,6,7,8]
results = Parallel.map(as, in_threads: 3) do |a|
# Parallel stuff
sleep rand
semaphore.synchronize {
# Sequential stuff
puts a
}
# Parallel stuff
sleep rand
end
You'll see that it prints stuff correctly but not necesarily in the same order. I used in_threads instead of in_processes (default) because Mutex doesn't work with processes. See below for an alternative if you do need processes.
References:
http://ruby-doc.org/core-2.2.0/Mutex.html
http://dev.housetrip.com/2014/01/28/efficient-cross-processing-locking-in-ruby/
In the interest of keeping it simple, here's what I'd do with built-in Thread:
results = files.map do |file|
result = Thread.new do
ftp.put(file)
end
end
Note that this code assumes that ftp.put(file) returns safely. If that isn't guaranteed, you'll have to do that yourself by wrapping calls in a timeout block and have each thread return an exception if one is thrown and then at the very end of the loop have a blocking check to see that results does not contain any exceptions.

Sinatra + Ruby: Random Number keeps changing every time I guess. Scope issue?

I am using Sinatra to build a WebGuesser with Jumpstart Labs. I enter a number into a text field in my browser. I click submit and I am supposed to get a response saying if my number is too low or too high (or within 5). I use Shotgun to load the server. I want to be able to guess a number without having the random number change every time I guess.
Code:
require 'sinatra'
require 'sinatra/reloader'
def check_guess(guess)
if params["guess"].to_i == guess
"You got it right!"
elsif params["guess"].to_i > guess
if params["guess"].to_i > (guess + 5)
"Way too high!"
else
"Close.. but too high!"
end
elsif params["guess"].to_i < guess
if params["guess"].to_i < (guess - 5)
"Way too low!"
else
"Close.. but too low!"
end
end
end
# Home route
get '/' do
SECRET_NUMBER = rand(100)
message = check_guess(SECRET_NUMBER)
erb :index, :locals => { :message => message }
end
Currently, I get a new random number every time I guess which doesn't help. I feel like it may have something to do with where my SECRET_NUMBER is scope-wise. Any thoughts?
Every time there is a GET request to "/", the relevant code is executed, which generates (with warnings) a new SECRET_NUMBER.
One way to deal with this is to route to different URLs for the first guess (in which case a secret number should be generated), and the consecutive guesses (in which case a new secret number should not be generated).
Also, it is very bad practice to use a constant for something that changes over time.
You could store the initial value in the user session, for that you would have to enable sessions in sinatra.
configure do
enable :sessions
set :session_secret, "somesecretstring"
end
After that you can create a number by going to a certain route
get '/random' do
session[:number] = rand(100)
end
You can then check your guesses on a different route
get '/checkguess' do
check_guess(session[:number]) unless session[:number].nil?
end
That's the basic thought, you'd have to define it further though. Hope it helps you a little
I was searching for the exact same question now and :
require "sinatra"
require "sinatra/reloader"
number = rand(100)
get '/' do
guess = params["guess"].to_i
message = check_guess(guess, number)
erb :index, :locals => {:bok => number, :alert => guess, :msg => message}
end
putting the rng outside the get block just worked. Generated number stays same until you change something in the code (even adding a space to the end and saving the file would work to re-random the number.) or restart the server completely.
About the constant(SECRET_NUMBER), it helps to give check_guess method only one argument since you define it as a constant at top. (since i'm new to ruby someone can correct me if i'm wrong.)
SECRET_NUMBER = rand(100)
get '/' do ... end
def check_guess(guess)
if guess < SECRET_NUMBER
"Your Guess is Too LOW!"
elsif guess > SECRET_NUMBER
"Your Guess is Too HIGH!"
else
"Conguratulations! You guessed it right:)"
end
For anyone still looking for the answer. rand should be defined outside of get block
require 'sinatra'
require 'sinatra/reloader'
rand = (rand() * 100).to_i
get '/' do
"The secret number is #{rand}"
end

Ruby Test:Unit, how to know fail/pass status for each test case in a test suite?

This question sounds stupid, but I never found an answer online to do this.
Assume you have a test suite like this page:
http://en.wikibooks.org/wiki/Ruby_Programming/Unit_testing
or code:
require "simpleNumber"
require "test/unit"
class TestSimpleNumber < Test::Unit::TestCase
def test_simple
assert_equal(4, SimpleNumber.new(2).add(2) )
assert_equal(4, SimpleNumber.new(2).multiply(2) )
end
def test_typecheck
assert_raise( RuntimeError ) { SimpleNumber.new('a') }
end
def test_failure
assert_equal(3, SimpleNumber.new(2).add(2), "Adding doesn't work" )
end
end
Running the code:
>> ruby tc_simpleNumber2.rb
Loaded suite tc_simpleNumber2
Started
F..
Finished in 0.038617 seconds.
1) Failure:
test_failure(TestSimpleNumber) [tc_simpleNumber2.rb:16]:
Adding doesn't work.
<3> expected but was
<4>.
3 tests, 4 assertions, 1 failures, 0 errors
Now, how to use a variable (what kind?) to save the testing results?
e.g., an array like this:
[{:name => 'test_simple', :status => :pass},
{:name => 'test_typecheck', :status => :pass},
{:name => 'test_failure', :status => :fail},]
I am new to testing, but desperate to know the answer...
you need to execute your test script file, that's it, the result will display pass or fails.
Suppose you save file test_unit_to_rspec.rb, after that execute below command
ruby test_unit_to_rspec.rb
Solved the problem with setting a high verbose level, in a test runner call.
http://ruby-doc.org/stdlib-1.8.7/libdoc/test/unit/rdoc/Test/Unit/UI/Console/TestRunner.html
require 'test/unit'
require 'test/unit/ui/console/testrunner'
class MySuperSuite < Test::Unit::TestSuite
def self.suite
suites = self.new("My Super Test Suite")
suites << TestSimpleNumber1
suites << TestSimpleNumber2
return suites
end
end
#run the suite
# Pass an io object
#new(suite, output_level=NORMAL, io=STDOUT)
runner = Test::Unit::UI::Console::TestRunner.new(MySuperSuite, 3, io)
results will be saved in the io stream in a nice format fo each test case.
What about using '-v' (verbose):
ruby test_unit_to_rspec.rb -v
This should show you a lot more information
You can check out another of Nat's posts for a way to capture the results. The short answer to your question is that there is no variable for capturing the results. All you get is:
Loaded suite My Special Tests
Started
..
Finished in 1.000935 seconds.
2 tests, 2 assertions, 0 failures, 0 errors
Which is not very helpful if you want to report to someone else what happened. Nat's other post shows how to wrap the Test::Unit in rspec to get a better result and more flexibility.
class Test::Unit::TestCase
def setup
#id = self.class.to_s()
end
def teardown
#test_result = "pass"
if(#_result.failure_count > 0 || #_result.error_count > 0)
#test_result = "fail"
# making sure no errors/failures exist before the next test case runs.
i = 0
while(i < #_result.failures.length) do
#_result.failures.delete_at(i)
i = i + 1
end
while(i < #_result.errors.length) do
#_result.errors.delete_at(i)
i = i + 1
end
#test_result = "fail"
end # if block ended
puts"#{#id}: #{#test_result}"
end # teardown definition ended
end # class Test::Unit::TestCase ended
Example Output :
test1: Pass
test2: fail
so on....

RSpec mocking an :each block

I want to use RSpec mocks to provide canned input to a block.
Ruby:
class Parser
attr_accessor :extracted
def parse(fname)
File.open(fname).each do |line|
extracted = line if line =~ /^RCS file: (.*),v$/
end
end
end
RSpec:
describe Parser
before do
#parser = Parser.new
#lines = mock("lines")
#lines.stub!(:each)
File.stub!(:open).and_return(#lines)
end
it "should extract a filename into extracted" do
linetext = [ "RCS file: hello,v\n", "bla bla bla\n" ]
# HELP ME HERE ...
# the :each should be fed with 'linetext'
#lines.should_receive(:each)
#parser.should_receive('extracted=')
#parser.parse("somefile.txt")
end
end
It's a way to test that the internals of the block work correctly by passing fixtured data into it. But I can't figure out how to do the actual feeding with RSpec mocking mechanism.
update: looks like the problem was not with the linetext, but with the:
#parser.should_receive('extracted=')
it's not the way it's called, replacing it in the ruby code with self.extracted= helps a bit, but feels wrong somehow.
To flesh out the how 'and_yield' works: I don't think 'and_return' is really what you want here. That will set the return value of the File.open block, not the lines yielded to its block. To change the example slightly, say you have this:
Ruby
def parse(fname)
lines = []
File.open(fname){ |line| lines << line*2 }
end
Rspec
describe Parser do
it 'should yield each line' do
File.stub(:open).and_yield('first').and_yield('second')
parse('nofile.txt').should eq(['firstfirst','secondsecond'])
end
end
Will pass. If you replaced that line with an 'and_return' like
File.stub(:open).and_return(['first','second'])
It will fail because the block is being bypassed:
expected: ["firstfirst", "secondsecond"]
got: ["first", "second"]
So bottom line is use 'and_yield' to mock the input to 'each' type blocks. Use 'and_return' to mock the output of those blocks.
I don't have a computer with Ruby & RSpec available to check this, but I suspect you need to add a call to and_yields call [1] on the end of the should_receive(:each). However, you might find it simpler not to use mocks in this case e.g. you could return a StringIO instance containing linetext from the File.open stub.
[1] http://rspec.rubyforge.org/rspec/1.1.11/classes/Spec/Mocks/BaseExpectation.src/M000104.html
I would go with the idea of stubbing the File.open call
lines = "RCS file: hello,v\n", "bla bla bla\n"
File.stub!(:open).and_return(lines)
This should be good enough to test the code inside the loop.
This should do the trick:
describe Parser
before do
#parser = Parser.new
end
it "should extract a filename into extracted" do
linetext = [ "RCS file: hello,v\n", "bla bla bla\n" ]
File.should_receive(:open).with("somefile.txt").and_return(linetext)
#parser.parse("somefile.txt")
#parser.extracted.should == "hello"
end
end
There are some bugs in the Parser class (it won't pass the test), but that's how I'd write the test.

Resources