【问题标题】:Can I realize dependency injection in my RSpec tests?我可以在我的 RSpec 测试中实现依赖注入吗?
【发布时间】:2018-05-29 16:28:38
【问题描述】:

我正在重构我的测试。我心想,如果正确的用户登录,我应该将负责处理的逻辑和我的成功行为的逻辑分开。所以我开始用我不成功的上下文创建一个共享示例,但我被困在了这一刻:

RSpec.shared_examples "changing status" do |arguments|
  /* ... */

  it_behaves_like "action requiring proper user logged in to be successful", action

  context "- when the required logged user is logged in" do
    before(:each) do
      /* ... */
    end
    context "and when the reservation has an allowed status" do
      /* ... */
    end
    context "and when the reservation has a not allowed status" do
      /* ... */
    end
  end
end
RSpec.shared_examples "action requiring proper user logged in to be successful" do |action|
  context "- when the logged in user is not the required logged user" do
    before(:each) do
      login(incidental_user)
    end
    it_behaves_like "unsuccessful attempt to change the reservation", action
  end
  context "- when there's no user logged in" do
    it_behaves_like "unsuccessful attempt to change the reservation", action
  end
end

所以,我想将代码从我的上下文 "when the required logged user is logged in" 注入到我的共享示例中以使其干净。我尝试使用匿名代码块和yield 关键字:

RSpec.shared_examples "changing status" do |arguments|
  /* ... */

  it_behaves_like "action requiring proper user logged in to be successful", action do
    before(:each) do
      /* ... */
    end
    context "and when the reservation has an allowed status" do
      /* ... */
    end
    context "and when the reservation has a not allowed status" do
      /* ... */
    end
  end

end
RSpec.shared_examples "action requiring proper user logged in to be successful" do |action|
  context "- when the required logged user is logged in" do
    yield
  end
  context "- when the logged in user is not the required logged user" do
    before(:each) do
      login(incidental_user)
    end
    it_behaves_like "unsuccessful attempt to change the reservation", action
  end
  context "- when there's no user logged in" do
    it_behaves_like "unsuccessful attempt to change the reservation", action
  end
end

但不幸的是它得到了这个错误:

LocalJumpError: 没有给出块(产量)

那么,我该怎么做呢?

【问题讨论】:

    标签: ruby-on-rails dependency-injection rspec


    【解决方案1】:

    有趣的问题。

    您可以在一个封装的示例中执行此操作,但还有另一种 IMO 更清晰的方式:

    为阳性和阴性测试分别创建shared_examples。你已经解决了消极的问题,所以你很好(只需更改名称,以明确这是一组消极的测试)。

    积极的可以是这样的:

    RSpec.shared_examples 'restricted action with logged in user' do
      before { login(authorized_user) }
    
      specify do
        expect_stuff_typical_for_logged_in_user
        # e.g. expect(response).to be_success
      end
    end
    

    然后在你的规范中包含include_examples,像这样

    context do 
      include_examples 'restricted action with logged in user'
      # you're "inside" the context of included examples so you can 
      # write down extra expectations specific for each action
    end
    

    您的积极部分可能没有共同的期望,只是设置上下文 - 您可以考虑使用shared_context 清楚地传达您的意图。

    您可以在一个共享示例中将其全部压缩,如下所示:

    RSpec.shared_examples "action requiring proper user logged in to be successful" do
      # set up the context for positive scenarios, so you can include it with 
      # `include_examples` and be "inside" this context
      before { login(authorized_user) }
    
      context "- when the logged in user is not the required logged user" do
        before(:each) do
          login(incidental_user)
        end
        it_behaves_like "unsuccessful attempt to change the reservation"
      end
      context "- when there's no user logged in" do
        before { log_out_people }
        it_behaves_like "unsuccessful attempt to change the reservation"
      end
    end
    

    这样做有两个问题。首先,你设置了一个积极的上下文,然后你必须在消极的例子中撤消它(感觉smelly)。 其次,规格中发生的事情可能并不明显。 它们看起来像这样:

    context do
      include_examples 'action requiring proper user logged in to be successful'
    
      specify do
        expect(response).not_to be_redirect
      end
    
      context 'when user have not seen changes TOS' do
        before { }
        specify do
          expect(session[:notice]).to eq 'Please read new TOS'
        end
    end 
    

    并不清楚是否涵盖了负面示例。

    然后,如果在任何地方您需要对 否定 案例的自定义期望 - 无论如何您都需要拆分它们。

    您可以决定采取任何一种方式,注意权衡取舍。

    【讨论】:

    • 我想我明白了,尽管您可以编辑您的示例以使其更具可读性。我猜你的意思是你想把第二个代码块的上下文而不是你的 cmets 放在你的第三个代码块中?我认为一个具有完整解决方案的块会更清楚。还有一个小问题:我在这里看不到任何依赖注入。我不能随意向我共享的示例注入任何内容。
    • include_examplesit_behaves_like 行为不同。我链接的文档中对此进行了解释。
    【解决方案2】:

    最后我用 lambda 解决了我的问题:

    RSpec.shared_examples "changing status" do |arguments|
      /* ... */
    
      it_behaves_like "action requiring proper user logged in to be successful", action, -> {
        before(:each) do
          /* ... */
        end
        context "and when the reservation has an allowed status" do
          /* ... */
        end
        context "and when the reservation has a not allowed status" do
          /* ... */
        end
      }
    end
    RSpec.shared_examples "action requiring proper user logged in to be successful" do |action, successful_behavior|
      context "- when the required logged user is logged in" do
        successful_behavior.()
      end
      context "- when the logged in user is not the required logged user" do
        before(:each) do
          login(incidental_user)
        end
        it_behaves_like "unsuccessful attempt to change the reservation", action
      end
      context "- when there's no user logged in" do
        it_behaves_like "unsuccessful attempt to change the reservation", action
      end
    end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-08
      • 1970-01-01
      • 2012-11-17
      • 1970-01-01
      • 1970-01-01
      • 2011-02-28
      相关资源
      最近更新 更多