NameError: uninitialized constant when referring to class within module - ruby

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.

Related

Testing a ruby module class method not working

I have a ruby module like this:
module SomeModule
module Account
def self.account_info
raise NotImplementedError
end
end
end
and this is my test:
describe ExchangeClientWrapper::Account do
let(:mock_class) do
class MockClass
extend SomeModule::Account
end
end
describe ".account_info" do
it "raises a NotImplementedError" do
expect { mock_class.account_info }.to raise_error(NotImplementedError)
end
end
end
end
I get this error:
expected NotImplementedError, got #<NoMethodError: undefined method `account_info' for MockClass:Class> with backtrace:
What is going on?
Does this not work:
module A
module B
def self.d
puts "hi there"
end
end
end
class C
extend A::B
end
C.d
But it appears this works:
module A
module B
def self.d
puts "hi there"
end
end
end
class C
include A
end
C::B.d
Reading the reference in this article which explains include, extend, and prepend and I found out that extends works with the Singleton class already so the self is unnecesary.
I made a small test with this code, which removes the self in the definition
module SomeModule
module Account
def account_info
raise NotImplementedError
end
end
end
class MockClass
extend SomeModule::Account
end
MockClass.account_info
And that raises NotImplementedError

Preventing error from causing RSpec test failure

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

RSpec: How to mock an object and methods that take parameters

I'm writing RSpec unit tests for a CommandLineInterface class that I've created for my Directory object. The CommandLineInterface class uses this Directory object to print out a list of people in my Directory. Directory has a #sort_by(param) method that returns an array of strings. The order of the strings depends on the param passed to the #sort_by method (e.g., sort_by("gender"). What would be the correct way to mock out this Directory behavior in my CLI specs? Would I use an instance_double? I am not sure how to do this for a method that takes parameters, like sorting by gender.
I'm only using Ruby and RSpec. No Rails, ActiveRecord, etc. being used here.
Snippets from the class and method I want to mock out:
class Directory
def initialize(params)
#
end
def sort_by(param)
case param
when "gender" then #people.sort_by(&:gender)
when "name" then #people.sort_by(&:name)
else raise ArgumentError
end
end
end
It all depends on how your objects are collaborating.
Some information is lacking in your question:
How does CommandLineInterface use Directory? Does it create an instance by itself or does it receive one as an argument?
Are you testing class methods or instance methods? (Prefer instance methods)
Here's how you could do it if you pass in the dependent object:
require 'rspec/autorun'
class A
def initialize(b)
#b = b
end
def foo(thing)
#b.bar(thing)
end
end
RSpec.describe A do
describe '#foo' do
context 'when given qux' do
let(:b) { double('an instance of B') }
let(:a) { A.new(b) }
it 'calls b.bar with qux' do
expect(b).to receive(:bar).with('qux')
a.foo('qux')
end
end
end
end
If the class initializes the dependant object and it isn't important to know which instance got the message you can do this:
require 'rspec/autorun'
B = Class.new
class A
def initialize
#b = B.new
end
def foo(thing)
#b.bar(thing)
end
end
RSpec.describe A do
describe '#foo' do
context 'when given qux' do
let(:a) { A.new }
it 'calls b.bar with qux' do
expect_any_instance_of(B).to receive(:bar).with('qux')
a.foo('qux')
end
end
end
end
If you just want to stub out the return value and not test whether the exact message was received, you can use allow:
require 'rspec/autorun'
B = Class.new
class A
def initialize
#b = B.new
end
def foo(thing)
thing + #b.bar(thing)
end
end
RSpec.describe A do
describe '#foo' do
context 'when given qux' do
let(:a) { A.new }
it 'returns qux and b.bar' do
allow_any_instance_of(B).to receive(:bar).with('qux') { 'jabber' }
expect(a.foo('qux')).to eq('quxjabber')
end
end
end
end

Ruby RSpec unit test gives undefined method error

I have created a unit test using RSpec.
The app.rb has:
module AppModule
class App
def get_item
str = self.get_string
puts "in get_item - #{str}"
end
def get_string
puts "hello, world"
end
end
end
The app_test.rb has:
require 'test_helper'
require 'env'
describe App do
before :each do
#var = App.new
end
describe "firsttest" do
it "should print string" do
#var.get_item
end
end
end
What I find is that get_item is called correctly. But when it gets to get_string, I get an error:
undefined method get_string for #App:0x2eaqc4600
Thanks.
Looks like a namespace issue. I don't know about your environment, but are you sure that #var is an instance of AppModule::App and not just ::App ?

FactoryGirl trouble

I have a class that is defined in the module.
module Mod
class Zed
include DataMapper::Resource
end
end
For testing, I define factory.
#/factories/zed.rb
FactoryGirl.define do
factory :zed do
#code
end
end
But when I start testing I get an error.
describe 'Zed' do
it "should have ..." do
FactoryGirl.create(:zed)
end
end
Error:
Failure/Error: FactoryGirl.create(:zed)
NameError:
uninitialized constant Zed
How to test a class that is included in the module?
Thanks.
You should specify class when defining a factory like this:
FactoryGirl.define do
factory :zed, class: Mod::Zed do
#code
end
end

Resources