Using Rspec in one file like test/unit - ruby

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

Related

Handling failures in data driven testing using rspec

I am using rspec to do some data driven testing. My test reads from a csv file, grabs an entry which is inserted into the text box on the page and is then compared to expected text which is also read from the csv file. All this is working as expected, I am able to read and compare without any issues.
Below is my code:
Method for reading csv file:
def user_data
user_data = CSV.read Dir.pwd + '/user_data.csv'
descriptor = user_data.shift
descriptor = descriptor.map { |key| key.to_sym }
user_data.map { |user| Hash[ descriptor.zip(user) ] }
end
Test:
describe "Text box tests" do
before :all do
#homepage = Homepage.new
end
it "should display the correct name" do
visit('http://my test url')
sleep 2
user_data.each do |entry|
#homepage.enter_name(entry[:name])
#homepage.click_go()
sleep 2
begin
expect(page).to have_css("#firstname", text: entry[:expected_name])
end
end
end
end
The problem is with failures. If I have a failure with one of the tests (i.e the expected text is not displayed on the page) then the test stops and all subsequent entries in the csv are not tested. If I put in a rescue after the expect statement like this:
rescue Exception => error
puts error.message
Then the error is logged to the console, however at the end of my test run it says no failures.
So basically I am looking for is, in the event of a failure for my test to keep running(until all entries in the csv have been covered), but for the test run to be marked as failed. Does anyone know how I can achieve this?
Try something like this:
context "when the user is on some page" do
before(:context) { visit('http://example.org/') }
user_data.each do |entry|
it "should display the correct name: #{entry[:name]}" do
#homepage.enter_name(entry[:name])
#homepage.click_go
expect(page).to have_css("#firstname", text: entry[:expected_name])
end
end
end
You will also need to change def user_data to def self.user_data
I would advise mapping over the entries and calling the regular capybara method has_css? instead of the rspec helper method. It would look like this:
results = user_data.map do |entry|
#homepage.enter_name(entry[:name])
#homepage.click_go()
sleep 2
page.has_css?("#firstname", text: entry[:expected_name])
end
expect(results.all?) to be_truthy
if you want to keep track of which ones failed, you cann modify it a bit:
missing_entries = []
user_data.each do |entry|
#homepage.enter_name(entry[:name])
#homepage.click_go()
sleep 2
has_entry = page.has_css?("#firstname", text: entry[:expected_name])
unless has_entry
missing_entries.push entry[:expected_name]
end
end
expect(missing_entries).to be_empty

Is it possible to write a spec for infinite-loop issues using Rspec?, 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

Unit Testing Chained Method Addition to Ruby Object

I'm in a situation where for our projects, there is a desire for 100% code coverage in our unit testing.
In order to support some dereference operations, I had to utilize the ability to chain dynamic method calls. So I ended up with this open class addition defined on one of my source files:
class Object
def call_method_chain(method_chain, arg=nil)
return self if method_chain.empty?
method_chain.split('.').inject(self) { |o,m|
if arg.nil?
o.send(m.intern)
else
o.send(m.intern, arg)
end
}
end
end
It works wonderfully. The problem is it's the only part of my application that isn't showing up as covered by unit tests, even though the area that calls the above method is covered.
I can't figure out how to cover the above with some unit tests.
I have no idea how much information is too much information here, but just to give an idea, here is the code that actually uses the above (in a file called data_setter.rb):
module Symbiont
module DataSetter
# #param data [Hash] the data to use
def using(data)
data.each do |key, value|
use_data_with(key, value) if object_enabled_for(key)
end
end
def use_data_with(key, value)
element = self.send("#{key}")
self.call_method_chain("#{key}.set", value) if element.class == Watir::TextField
self.call_method_chain("#{key}.set") if element.class == Watir::Radio
self.call_method_chain("#{key}.select", value) if element.class == Watir::Select
return self.call_method_chain("#{key}.check") if element.class == Watir::CheckBox and value
return self.call_method_chain("#{key}.uncheck") if element.class == Watir::CheckBox
end
private
def object_enabled_for(key)
web_element = self.send("#{key}")
web_element.enabled? and web_element.visible?
end
end
end
You can see using() calls use_data_with() and it's that latter method that relies on the call_method_chain() that I defined on Object.
Further, I have a unit test that exercises the using() method, which looks like this:
require 'spec_helper'
describe Symbiont::DataSetter do
include_context :page
include_context :element
it 'will set data in a text field' do
expect(watir_element).to receive(:visible?).and_return(true)
expect(watir_element).to receive(:enabled?).and_return(true)
expect(watir_browser).to receive(:text_field).with({:id => 'text_field'}).exactly(2).times.and_return(watir_element)
watir_definition.using(:text_field => 'works')
end
end
That test shows my logic calling the using() method. That test passes. When looking at my code coverage, the coverage of data_setter.rb is 100%. Yet the coverage report also shows that my call_method_chain() method is never being executed.
This is one of those cases where I fear strict adherence to unit testing coverage is causing more trouble than it's worth since I know the above logic is working.
EDIT (based on response from Neil):
I'm using SimpleCov for the coverage.
Regarding the coverage report and object, taking this:
1 class Object
2 def call_method_chain(method_chain, arg=nil)
3 return self if method_chain.empty?
4 method_chain.split('.').inject(self) { |o,m| #o.send(m.intern, arg) }
5 if arg.nil?
6 o.send(m.intern)
7 else
8 o.send(m.intern, arg)
9 end
10 }
11 end
12 end
The report says that lines 1 and 2 are covered. They show up as green. Starting from 3 down to 9, it's all red.
Regarding my spec_helper.rb file, this is what it looks like, at the top:
require 'simplecov'
require 'coveralls'
Coveralls.wear!
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
SimpleCov::Formatter::HTMLFormatter,
Coveralls::SimpleCov::Formatter
]
SimpleCov.start do
add_filter '/spec'
coverage_dir "#{SimpleCov.root}/spec/reports/coverage"
minimum_coverage 90
maximum_coverage_drop 5
end
require 'symbiont'
I have found that if I make a simple test like this:
it 'allows methods to be chained' do
methods = 'testing'.call_method_chain("reverse.capitalize")
end
That covers up to line 6 of my Object insertion. That leaves line 8, which I can't quite get to work. I tried this:
methods = 'testing'.call_method_chain("reverse.capitalize.eql?", 'GNITSET')
To no avail, however. So I can actually get it down to only one line not being covered, assuming I just use those little point tests like that. I'm not sure if that's in the spirit of good unit testing.

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