问题
require './spec/spec_helper'
require './bank'
describe Bank do
context "#transfer" do
before(:all) do
@customer1 = Customer.new(500)
customer2 = Customer.new(0)
@customer1.stub(:my_money).and_return(1000)
customer2.stub(:my_money).and_return(0)
@transfer_message = Bank.new.transfer(@customer1, customer2, 2000)
end
it "should return insufficient balance if transferred amount is greater than balance" do
expect(@transfer_message).to eq("Insufficient funds")
end
it "calls my_money" do
expect(@customer1).to have_received(:my_money)
end
end
end
When I use before(:each)
instead before(:all)
it works. But if use before(:all)
it throws error as undefined method proxy_for for nil:NilClass
. I couldn't find out the reason. Could you please help me? Thanks in advance.
回答1:
Late to the party? Yeah, but wouldn't mind to drop my own one cent from what I discovered. I encountered a similar error while trying to stub a request in a RSpec.configure
block so that the stub will only be available to examples I pass the config.around(:each, option)
option to.
So, that means I was using the stub outside the scope of individual examples which is not supported by RSpec::Mocks
here!. A work around is to use a temporary scope in the context.
So you have
before(:all) do
RSpec::Mocks.with_temporary_scope do
@customer1 = Customer.new(500)
customer2 = Customer.new(0)
@customer1.stub(:my_money).and_return(1000)
customer2.stub(:my_money).and_return(0)
@transfer_message = Bank.new.transfer(@customer1, customer2, 2000)
end
end
HTH!
回答2:
before(:all)
is not deprecated, but use of doubles from rspec-mocks in before(:all)
is not supported. See the referenced issues in github issue for background.
The current master
version of rspec-mocks, to be available with 3.0.0.beta2, will fail with the following error:
The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported.
Prior versions will generate the undefined method proxy_for ...
error at the point of stubbing.
回答3:
This should work:
require "rspec/mocks/standalone"
before(:all) do
回答4:
I'm not sure if it's as canonical/best-practice as @El'Magnifico's answer using RSpec::Mocks.with_temporary_scope, but I found that by using an around
hook instead of before
, I was able to dynamically inject before
blocks (and similar) into the example_group
, resulting in them running alongside my test:
- https://relishapp.com/rspec/rspec-core/docs/hooks/around-hooks
Example
# spec/spec_helper.rb
RSpec.configure do |config|
# ..snip..
config.include RequestForgerySpecHelper, type: :feature
# ..snip..
end
# spec/support/request_forgery_spec_helper.rb
module RequestForgerySpecHelper
def self.included(base)
base.around(:all) do |ex|
puts "around all start"
ex.example_group.prepend_before do
puts "around all inside prepend_before"
end
ex.example_group.append_after do
puts "around all inside append_after"
end
ex.run
puts "around all end"
end
end
end
Now if we assume our test already prints some logs before
, after
, and inside the test
itself, our output looks like this:
around all start
around all inside prepend_before
inside spec file before
inside spec file test
inside spec file after
around all inside append_after
around all end
Because we are now dynamically injecting our before
hook into the same context as our tests, we no longer see the following error:
before
is not available from within an example (e.g. anit
block) or from constructs that run in the scope of an example (e.g.before
,let
, etc). It is only available on an example group (e.g. adescribe
orcontext
block)
Going Deeper
The ex
parameter provided to the around
hook is an instance of RSpec::Core::Example::Procsy, which gives access to the RSpec::Core::Example instance through the example attribute.
RSpec::Core::Example
gives us access to the RSpec::Core::ExampleGroup
associated with this test via the example_group method.
RSpec::Core::ExampleGroup
gives us access to the DSL/methods allowing us to dynamically define before
/after
/let
/etc blocks without triggering the not available from within an example
error; as we can see by listing the methods in pry:
ls RSpec::Core::ExampleGroup
# RSpec::Core::Hooks#methods: after append_after append_before around before hooks prepend_after prepend_before
# RSpec::Core::MemoizedHelpers::ClassMethods#methods: let let! subject subject!
# RSpec::Core::ExampleGroup.methods:
# add_example currently_executing_a_context_hook? define_nested_shared_group_method describe ensure_example_groups_are_configured fcontext ffeature fit fspecify include_examples location parent_groups run scenario specify superclass_metadata update_inherited_metadata xexample xspecify
# before_context_ivars declaration_locations delegate_to_metadata described_class example fdescribe file_path focus id it metadata pending run_after_context_hooks set_it_up store_before_context_ivars top_level? with_replaced_metadata xfeature
# children define_example_group_method descendant_filtered_examples description example_group feature filtered_examples for_filtered_examples idempotently_define_singleton_method it_behaves_like next_runnable_index_for remove_example run_before_context_hooks set_ivars subclass top_level_description xcontext xit
# context define_example_method descendants each_instance_variable_for_example examples fexample find_and_eval_shared fscenario include_context it_should_behave_like ordering_strategy reset_memoized run_examples skip superclass_before_context_ivars traverse_tree_until xdescribe xscenario
来源:https://stackoverflow.com/questions/21235269/method-stubbing-on-beforeall