How do I correctly include and test an ActiveSupport::Concern in an RSpec mocked_model? - ruby

I'm trying to spec a module by including it in a basic mock_model object. However, when I call the instance method defined in the module ActiveRecord tries to establish a connection with the database.
The module:
module Stuff
module SoftDelete
extend ActiveSupport::Concern
def soft_delete
puts "Called here"
end
end
end
The Spec:
describe Stuff::SoftDelete do
class Network < ActiveRecord::Base
include Stuff::SoftDelete
attr_accessor :deleted_at
end
before (:each) do
#network = mock_model(Network)
end
context "When a record is deleted" do
it "is marked as deleted" do
#network.soft_delete
end
end
end
When I run this Spec, the following error occurs:
1) Stuff::SoftDelete When a record is deleted is marked as deleted
Failure/Error: #network.soft_delete
ActiveRecord::ConnectionNotEstablished:
ActiveRecord::ConnectionNotEstablished
# ./spec/apoc/soft_delete_spec.rb:18:in `block (3 levels) in <top (required)>'
Note: If I include the SoftDelete module in a real ActiveRecord class, it will work. It just seems that mock_model isn't able to deal with the module.
Would love some help on this one.
Thanks!

Do you trust ActiveRecord? If so, don't inherit from it; test your module in isolation. If your module includes calls ActiveRecord methods, stub them and test only your code.

Related

NameError: uninitialized constant error

Given the following code:
module Backup
module Destination
class Base
def initialize
puts 'Base'
end
end
end
end
module Backup
module Destination
class Test < Base
def initialize
puts 'Test'
super
end
end
end
end
Backup::Destination::Test.new
This works as expected, outputting:
Test
Base
However if I split things up like this:
# lib/backup.rb
require_relative 'backup/destination/base'
module Backup; end
# lib/backup/destination/base.rb
require_relative 'test'
module Backup
module Destination
class Base
def initialize
puts 'Base'
end
end
end
end
# lib/backup/destination/test.rb
module Backup
module Destination
class Test < Base
def initialize
puts 'Test'
super
end
end
end
end
And execute with the following (from irb):
require_relative 'lib/backup'
I get this error:
NameError: uninitialized constant Backup::Destination::Base
from /lib/backup/destination/test.rb:3:in `<module:Destination>'
from /lib/backup/destination/test.rb:2:in `<module:Backup>'
from /lib/backup/destination/test.rb:1:in `<top (required)>'
from /lib/backup/destination/base.rb:1:in `require_relative'
from /lib/backup/destination/base.rb:1:in `<top (required)>'
from /lib/backup.rb:1:in `require_relative'
from /lib/backup.rb:1:in `<top (required)>'
from (irb):1:in `require_relative'
What am I missing?
Note: I couldn't post the above without adding more details. Stupid feature because in this case code is worth a thousand words. (this text allowed the question to be posted)
The problem is that you are requiring test.rb before your Base class is defined. One possible solution is to move your require to the bottom of base.rb.
Another possible solution is to remove your require from base and require both files in the correct order from backup.
Made the following changes to fix the problem:
# lib/backup.rb
require_relative 'backup/destination/base'
require_relative 'backup/destination/test'
module Backup; end
And removed the require_relative statement from lib/backup/destination/base.rb. This fixed the order of the require_relative statements. I mistakenly thought the files were required before anything was executed.

Rspec validation of method definition - Failure/Error

In Rspec, testing whether an instance is able to call method x.
DockingStation.rb
class DockingStation
def release_bike
end
end
Docking_spec.rb
require_relative '../lib/DockingStation'
describe DockingStation do
before(:each) do
#dockstat = DockingStation.new
end
describe "#DockingStation" do
it "Check release method" do
expect(#dockstat).to respond_to(:release_bike)
end
end
end
Currently getting the following error message:
1) DockingStation#DockingStation Check release method
Failure/Error: expect(#dockstat).to respond_to(:release_bike)
expected #<DockingStation:0x007fa518a6da00> to respond to :release_bike
# ./spec/Docking_spec.rb:10:in `block (3 levels) in <top (required)>'
What I'm expecting is for the object #dockstat instantiated in the Docking_spec.rb to respond to the release_bike method defined in DockingStation.rb, but this is not the case.
require_relative '../DockingStation'

Can't understand why I'm getting a `initialize': uninitialized constant Controller::View (NameError)

I'm trying to understand why I'm getting this error and I suspect it's because I have my Controller class and View class in two separate Ruby files. I was told that using require_relative 'filename' should reference all the code from one file into another, but I seem to be missing something. Okay here goes,
In controller.rb file, I have
require_relative 'view'
require_relative 'deck_model'
require_relative 'flashcard_model'
class Controller
def initialize
#deckofcards = Deck.new
#welcome = View.new.welcome
#player_guess = View.new.get_user_guess
#success_view = View.new.success
#failure_view = View.new.failure
end
def run
#Logic to run the game
# #current_card
# #user_guess
puts "Let's see if this prints"
# pull_card_from_deck
end
end
In my view.rb file, I have,
require_relative 'controller'
class View
attr_accessor :userguess
def initialize (userguess = " ")
#userguess = userguess
end
def welcome
system ("clear")
puts "Welcome! Let's play a game."
puts "I'll give you a definition and you have to give me the term"
puts "Ready..."
end
def get_user_guess
#userguess = gets.chomp.downcase
end
def success
puts "Excellent! You got it."
end
def failure
puts "No, that's not quite right."
end
end
However when I run controller.rb, I get the following error,
/Users/sean/Projects/flash/source/controller.rb:11:in `initialize': uninitialized constant Controller::View (NameError)
from /Users/sean/Projects/flash/source/controller.rb:51:in `new'
from /Users/sean/Projects/flash/source/controller.rb:51:in `<top (required)>'
from /Users/sean/Projects/flash/source/view.rb:1:in `require_relative'
from /Users/sean/Projects/flash/source/view.rb:1:in `<top (required)>'
from controller.rb:1:in `require_relative'
from controller.rb:1:in `<main>'
Can anyone please help me figure this out.
You did not post your full code, but it sounds like this is an error caused by the circular dependencies you specified in your project. You have view.rb depending on controller.rb and controller.rb depending on view.rb. The Ruby interpreter will not execute these files simultaneously; it has to execute one and then execute the other.
It looks like it is executing controller.rb first, but it sees that view.rb is required, so it starts executing that. Then in view.rb it sees that controller.rb is required, so it starts executing controller.rb again. Then at some point in controller.rb, you must be creating a new instance of the Controller class. But we aren't done defining the View class yet, so View is undefined and you get an exception while trying to create that controller.
To fix this, you should consider not creating any Controller or View objects until both of the classes are fully loaded.
+1 to #DavidGrayson comment.
If my assumption is correct, your issue is with require_relative 'controller' in your view.rb file.
If you see, it looks like View is requiring Controller then Controller gets loaded which seems to be sending new somewhere to Controller which then sends new to View but it hasn't been completely required.

Rails 4: Undefined method on module

I have a module in app/misc/dsl/builder.rb that has this code
module Dsl
class Builder
def initialize(context, &block)
return if not block_given?
parent_context = block.binding.eval "self"
parent_context.extend Proxy
parent_context.object = context
parent_context.instance_eval &block
end
end
def self.set_context(context, &block)
Dsl::Builder.new(context, &block)
end
end
Note: this directory misc is preloaded in application.rb
config.autoload_paths += Dir[Rails.root.join('app', 'models', '{**/}'),
Rails.root.join('app', 'misc', '{**/}')
]
Then, somewhere in the text (lets say at foo.rb) I have this code:
Dsl.set_context(obj) do
#some code with obj receiving messages
end
The test stack we are using consists on Zeus+Guard+Rspec. Now, lets say I rewrite the code to something not working
Dsl.set_context(obj) do
asdqwe #this message does not exists
end
From times to times, I receive this baffling message
1) SomeOtherClass search_hash receiving keywords params should query for those keywords
Failure/Error: subject.search_hash
NoMethodError:
undefined method `set_context' for Dsl:Module
# ./app/misc/product_query.rb:116:in `base_search_hash'
# ./app/misc/product_query.rb:25:in `search_hash'
# ./spec/misc/product_query_spec.rb:78:in `block (4 levels) in <top (required)>'
# -e:1:in `<main>'
instead of the correct message that should be regarding undefined method asdqwe
Any clue about this?
Look here
it says:
Rails 3 has been updated such that classes/modules (henceforth, C/M)
are lazy loaded from the autoload paths as they are needed
so, you can do require_relative 'app/misc/dsl/builder.rb' in your rspec_helper.rb (can it be better with just require?) The problem must be that the loader doesn't know in advance where to find Dsl.set_context, but he will know once you have referenced Dsl::Builder
Hope it helps

why is before :save callback hook not getting called from FactoryGirl.create()?

This simple example uses DataMapper's before :save callback (aka hook) to increment callback_count. callback_count is initialized to 0 and should be set to 1 by the callback.
This callback is invoked when the TestObject is created via:
TestObject.create()
but the callback is skipped when created by FactoryGirl via:
FactoryGirl.create(:test_object)
Any idea why? [Note: I'm running ruby 1.9.3, factory_girl 4.2.0, data_mapper 1.2.0]
Full details follow...
The DataMapper model
# file: models/test_model.rb
class TestModel
include DataMapper::Resource
property :id, Serial
property :callback_count, Integer, :default => 0
before :save do
self.callback_count += 1
end
end
The FactoryGirl declaration
# file: spec/factories.rb
FactoryGirl.define do
factory :test_model do
end
end
The RSpec tests
# file: spec/models/test_model_spec.rb
require 'spec_helper'
describe "TestModel Model" do
it 'calls before :save using TestModel.create' do
test_model = TestModel.create
test_model.callback_count.should == 1
end
it 'fails to call before :save using FactoryGirl.create' do
test_model = FactoryGirl.create(:test_model)
test_model.callback_count.should == 1
end
end
The test results
Failures:
1) TestModel Model fails to call before :save using FactoryGirl.create
Failure/Error: test_model.callback_count.should == 1
expected: 1
got: 0 (using ==)
# ./spec/models/test_model_spec.rb:10:in `block (2 levels) in <top (required)>'
Finished in 0.00534 seconds
2 examples, 1 failure
At least for factory_girl 4.2 (don't know since which version it is supported), there is another workwaround through the use of custom methods to persist objects. As it is stated in a response to an issue about it in Github, it is just a matter of calling save instead of save!.
FactoryGirl.define do
to_create do |instance|
if !instance.save
raise "Save failed for #{instance.class}"
end
end
end
Of course it is not ideal because it should be functional in FactoryGirl core, but I think right now it is the best solution and, at the moment, I'm not having conflicts with other tests...
The caveat is that you have to define it in each factory (but for me it wasn't an inconvenient)
Solved.
#Jim Stewart pointed me to this FactoryGirl issue where it says "FactoryGirl calls save! on the instance [that it creates]". In the world of DataMapper, save! expressly does not run the callbacks -- this explains the behavior that I'm seeing. (But it doesn't explain why it works for #enthrops!)
That same link offers some workarounds specifically for DataMapper and I'll probably go with one of them. Still, it would be nice if an un-modified FactoryGirl played nice with DataMapper.
update
Here's the code suggested by Joshua Clayton of thoughtbot. I added it to my spec/factories.rb file and test_model_spec.rb now passes without error. Cool beans.
# file: factories.rb
class CreateForDataMapper
def initialize
#default_strategy = FactoryGirl::Strategy::Create.new
end
delegate :association, to: :#default_strategy
def result(evaluation)
evaluation.singleton_class.send :define_method, :create do |instance|
instance.save ||
raise(instance.errors.send(:errors).map{|attr,errors| "- #{attr}: #{errors}" }.join("\n"))
end
#default_strategy.result(evaluation)
end
end
FactoryGirl.register_strategy(:create, CreateForDataMapper)
update 2
Well. perhaps I spoke too soon. Adding the CreateForDataMapper fixes that one specific test, but appears to break others. So I'm un-answering my question for now. Someone else have a good solution?
Use build to build your object, then call save manually...
t = build(:test_model)
t.save

Resources