Preventing error from causing RSpec test failure - ruby

Suppose I have a class with methods like these:
class MyClass
...
def self.some_class_method
my_instance = MyClass.new
self.other_class_method(my_instance)
raise 'ERROR'
end
def self.other_class_method(instance)
...
end
end
And the test for it looks like this:
require 'spec_helper'
describe MyClass do
describe '.some_class_method' do
context 'testing some_class_method' do
it 'calls other_class_method' do
MyClass.should_receive(:other_class_method)
MyClass.some_class_method
end
end
end
end
The test errors out with ERROR, and if I remove the raise 'ERROR' line, the test passes. But here I want to only test whether some_class_method calls other_class_method, regardless of what happens afterwards. I could change it to expect the method to raise an error, but that's not the purpose of this particular test. Is there a better way?

You could rescue the exception in the test.
describe MyClass do
describe '.some_class_method' do
context 'testing some_class_method' do
it 'calls other_class_method' do
MyClass.should_receive(:other_class_method)
begin
MyClass.some_class_method
rescue
end
end
end
end
end

What about adding an expectation that the method is raising an error. That will even enhance your testing:
describe MyClass do
describe '.some_class_method' do
context 'testing some_class_method' do
it 'calls other_class_method' do
expect(MyClass).to receive(:other_class_method)
expect { MyClass.some_class_method }.to raise_error("ERROR")
end
end
end
end

Related

How to use RSpec to test that a method catches a symbol?

How can you test that a method catches a thrown symbol in RSpec? I have two methods that interact with each other through #throw and #catch. I already figured out how to test that the symbol is thrown on one end:
expect { subject.method_a }.to throw_symbol(:some_symbol)
Now I want to test that method_b catches the thrown symbol, which I imagine might look something like this:
expect { subject.method_b }.to catch_symbol(:some_symbol)
Only that doesn't work. So my question is, how can you test that a method catches a symbol in RSpec?
EDIT: Here's a very basic example of method_a and method_b, stripped of all logic not related to the problem at hand.
def method_a
throw :some_symbol
end
def method_b
catch :some_symbol do
method_a
end
end
catch and throw are methods on Kernel so you can expect them as usual:
class SomeClass
def a
throw :foo
end
def b
catch :foo do
a
end
end
end
RSpec.describe "" do
it "" do
inst = SomeClass.new
expect(inst).to receive(:throw).with(:foo)
inst.a
expect(inst).to receive(:catch).with(:foo)
inst.b
end
end

NameError: uninitialized constant when referring to class within module

I have the following tests, written using RSpec
spec/services/aquatic/factory_spec.rb
describe Hobby::Aquatic::Factory do
let(:factory) { Hobby::Aquatic::Factory.instance }
describe "singleton" do
it "should be the same object" do
Hobby::Aquatic::Factory.instance.should be factory
end
it "raise error if given junk" do
expect {factory.hobby("junk")}.to raise_error
end
end
end
spec/services/aquatic/hobbies_spec.rb
describe Hobby::Aquatic do
it "creates fishing" do
expect { Hobby::Aquatic::Fishing.new }.to_not raise_error
end
end
and have defined the following module / classes
app/services/aquatic/factory.rb
require 'singleton'
module Hobby
module Aquatic
class Factory
include Singleton
def self.instance
##instance ||= new
end
def hobby(name)
return Fishing.new if name == "fishing"
return Surfing.new if name == "surfing"
raise ArgumentError, "Unknown hobby for supplied name"
end
end
end
end
app/services/aquatic/hobbies.rb
module Hobby
module Aquatic
class Fishing
end
class Surfing
end
end
end
When I run the tests the Factory tests all pass fine, but the test of the Hobby::Aquatic::Fishing object results in:
Failure/Error: expect { Hobby::Aquatic::Fishing.new }.to_not raise_error
expected no Exception, got #<NameError: uninitialized constant Hobby::Aquatic::Fishing> …
What have I done wrong?
add require_relative 'hobbies' below require 'singleton' to fix this.
I am not sure why Rails is loading the factory but not the hobbies automagically but this works.

how to access instant variable with rspec

I have a class like this.
require 'net/http'
class Foo
def initialize
#error_count = 0
end
def run
result = Net::HTTP.start("google.com")
#error_count = 0 if result
rescue
#error_count += 1
end
end
And I want to count up #error_count if connection fails, so I wrote like this.
require_relative 'foo'
describe Foo do
before(:each){#foo = Foo.new}
describe "#run" do
context "when connection fails" do
before(:each){ Net::HTTP.stub(:start).and_raise }
it "should count up #error_count" do
expect{ #foo.run }.to change{ #foo.error_count }.from(0).to(1)
end
end
end
end
Then I got this error.
NoMethodError:
undefined method `error_count' for #<Foo:0x007fc8e20dcbd8 #error_count=0
How can I access instance variable with Rspec?
Edit
describe Foo do
let(:foo){ Foo.new}
describe "#run" do
context "when connection fails" do
before(:each){ Net::HTTP.stub(:start).and_raise }
it "should count up #error_count" do
expect{ foo.run }.to change{foo.send(:error_count)}.from(0).to(1)
end
end
end
end
Try #foo.send(:error_count) i guess it should work.
Update: found in docs
expect{ foo.run }.to change{foo.instance_variable_get(:#error_count)}.from(0).to(1)

how to test ruby inheritance with rspec

I am trying to test logic that runs during class inheritance, but have run into an issue when running multiple assertions.
i first tried...
describe 'self.inherited' do
before do
class Foo
def self.inherited klass; end
end
Foo.stub(:inherited)
class Bar < Foo; end
end
it 'should call self.inherited' do
# this fails if it doesn't run first
expect(Foo).to have_received(:inherited).with Bar
end
it 'should do something else' do
expect(true).to eq true
end
end
but this fails because the Bar class has already been loaded, and therefore does not call inherited a 2nd time. If the assertion doesn't run first... it fails.
So then i tried something like...
describe 'self.inherited once' do
before do
class Foo
def self.inherited klass; end
end
Foo.stub(:inherited)
class Bar < Foo; end
end
it 'should call self.inherited' do
#tested ||= false
unless #tested
expect(Foo).to have_receive(:inherited).with Bar
#tested = true
end
end
it 'should do something else' do
expect(true).to eq true
end
end
because #tested doesn't persist from test to test, the test doesn't just run once.
anyone have any clever ways to accomplish this? This is a contrived example and i dont actually need to test ruby itself ;)
Here's an easy way to test for class inheritance with RSpec:
Given
class A < B; end
a much simpler way to test inheritance with RSpec would be:
describe A do
it { expect(described_class).to be < B }
end
For something like this
class Child < Parent; end
I usually do:
it 'should inherit behavior from Parent' do
expect(Child.superclass).to eq(Parent)
end
For some reason, I did not manage the solution from David Posey to work (I guess I did something wrong. Feel free to provide a solution in the comments). In case someone out there have the same problem, this works too:
describe A
it { expect(described_class.superclass).to be B }
end
Make your class definition for testing inheritance run during the test:
describe 'self.inherited' do
before do
class Foo
def self.inherited klass; end
end
# For testing other properties of subclasses
class Baz < Foo; end
end
it 'should call self.inherited' do
Foo.stub(:inherited)
class Bar < Foo; end
expect(Foo).to have_received(:inherited).with Bar
end
it 'should do something else' do
expect(true).to eq true
end
end
Another way:
class Foo; end
class Bar < Foo; end
class Baz; end
RSpec.describe do
it 'is inherited' do
expect(Bar < Foo).to eq true
end
it 'is not inherited' do
expect(Baz < Foo).not_to eq true
end
end

Ruby unit tests: run some code after each failed test

Is there some clean and elegant way to execute my code right after a failed assert in ruby unit tests in Test::Unit, before teardown gets executed?
I am doing some automated GUI testing and would like to take a screenshot right after something failed.
If you're on 1.9, don't use Test::Unit::TestCase as your base class. Subclass it and override #run_test to rescue, take the screenshot and reraise:
class MyAbstractTestCase < Test::Unit::TestCase
def run_test( *args )
super(*args)
rescue
snapshot()
raise
end
end
Alternatively, and I think this is actually the most terse way in, you can use a before_teardown hook:
class MyTestCase < Test::Unit::TestCase
add_teardown_hook do |tc|
screenshot() unless tc.passed?
end
end
This won't work on 1.8's test/unit, but will with the minitest in 1.9.
Well you could extend Test::Unit::Assertions to do what you like, i do not think there is a built-in way to do this. Perhaps something like this (quick & dirty):
require 'test/unit'
module Test::Unit::Assertions
def safe_assert(test, msg=nil)
passed = msg.nil? ? assert(test) : assert(test,msg)
ensure
puts 'take screenshot' unless passed
end
end
class MyTest < Test::Unit::TestCase
def setup
puts 'setup'
end
def teardown
puts 'teardown'
end
def test_something
safe_assert true
puts 'before failing assert'
safe_assert false, "message"
puts 'after failing assert'
end
end
output:
Loaded suite unittest
Started
setup
before failing assert
take screenshot
teardown
F
Finished in 0.001094 seconds.
1) Failure:
test_something(MyTest) [unittest.rb:5]:
message
1 tests, 2 assertions, 1 failures, 0 errors, 0 skips
Test run options: --seed 58428
EDIT: you could actually pass the args to assert in a simpler way:
module Test::Unit::Assertions
def safe_assert(*args)
passed = assert(*args)
ensure
puts 'take screenshot' unless passed
end
end
also, you could wrap a standard assertin a begin-ensure-end block if you only need this functionality infrequently:
class MyTest < Test::Unit::TestCase
def test_something
safe_assert true
puts 'before failing assert'
begin
passed = assert false, "message"
ensure
puts 'take screenshot' unless passed
end
puts 'after failing assert'
end
end
or you build a method that ensures a screenshot like in the following example. This actually seems like the cleanest way to me:
def screenshot_on_fail
passed = yield
ensure
puts 'take screenshot' unless passed
end
class MyTest < Test::Unit::TestCase
def test_something_else
screenshot_on_fail do
assert true
end
screenshot_on_fail do
assert false, 'message'
end
end
end

Resources