【问题标题】:method stubbing on before(:all)在 before(:all) 上存根的方法
【发布时间】:2014-01-20 13:23:24
【问题描述】:
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

当我使用 before(:each) 而不是 before(:all) 时,它可以工作。但是如果使用before(:all),它会抛出undefined method proxy_for for nil:NilClass 的错误。我找不到原因。请你帮助我好吗?提前致谢。

【问题讨论】:

  • 我记得before(:all) 已被贬低并被before do ... 取代也是更正确的方法是不要使用变量,您可以使用let 代替。如果你有 nil:NilClass 这意味着 rspec 没有在数据库中创建客户记录(它可以通过调用 let!(:object) { action } 来解析)类似这样的东西。

标签: rspec rspec-rails


【解决方案1】:

聚会迟到了?是的,但不介意从我发现的东西中放弃我自己的一分钱。我在尝试在 RSpec.configure 块中存根请求时遇到了类似的错误,因此存根仅适用于我将 config.around(:each, option) 选项传递给的示例。

所以,这意味着我在单个示例范围之外使用了存根,RSpec::Mockshere! 不支持该存根。一种解决方法是在上下文中使用临时范围。

所以你有

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!

【讨论】:

  • 这太棒了,可能应该在核心 IMO 中,例如“当使用标记示例的子集时,这应该没问题并在临时范围内执行”。
【解决方案2】:

before(:all) 不被弃用,但不支持在before(:all) 中使用来自 rspec-mocks 的双精度。有关背景,请参阅github issue 中的参考问题。

rspec-mocks 的当前master 版本(可用于 3.0.0.beta2)将失败并出现以下错误:

The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported.

以前的版本会在存根处生成undefined method proxy_for ... 错误。

【讨论】:

  • 出于好奇你能告诉我为什么他们不支持这个先前的 rspec3 吗?
  • 查看 github issue 中的链接,您将获得一些背景知识。
【解决方案3】:

这应该可行:

require "rspec/mocks/standalone"
before(:all) do

【讨论】:

  • 小心。这开始给我更多的错误重新运行我的测试用例:SQLite3::ConstraintException: UNIQUE constraint。也许添加对独立功能的解释会很好。通过在我的测试文件顶部设置debugger 解决了这个问题,使用Model.destroy_all 触发时删除了所有模型记录,还必须使用Base.connection.execute 删除sql_sequence。
【解决方案4】:

我不确定它是否像使用RSpec::Mocks.with_temporary_scope@El'Magnifico's answer 一样规范/最佳实践,但我发现通过使用around 挂钩而不是before,我能够动态注入@987654331 @ 块(和类似的)进入example_group,导致它们与我的测试一起运行:

示例

# 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

现在,如果我们假设我们的测试已经打印了一些日志 beforeaftertest 本身,我们的输出如下所示:

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

因为我们现在将 before 挂钩动态注入到与测试相同的上下文中,所以我们不再看到以下错误:

before 在示例(例如 it 块)或在示例范围内运行的构造(例如 beforelet 等)中不可用。它仅适用于示例组(例如 describecontext 块)

深入研究

提供给around 挂钩的ex 参数是RSpec::Core::Example::Procsy 的一个实例,它允许通过example 属性访问RSpec::Core::Example 实例。

RSpec::Core::Example 让我们可以通过example_group 方法访问与此测试关联的RSpec::Core::ExampleGroup

RSpec::Core::ExampleGroup 允许我们访问 DSL/方法,允许我们动态定义 before/after/let/etc 块,而不会触发 not available from within an example 错误;我们可以通过列出 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多