是否可以在通话期间传递参数让let阻塞?

时间:2019-09-17 14:35:46

标签: ruby-on-rails rspec ruby-on-rails-5 rspec3

我已经开始使用Rspec,现在我编写了一些成功的工作并且非常困难的测试。但是,由于需要更多练习,我几次重构了这些测试。

在Rspec的文档中找不到我的问题的答案,这就是为什么我在这里。

问题与指令let有关,该指令提供了不仅可以通过首次调用返回某些对象的功能,而且还可以。

我当前的rspec代码部分是:

RSpec.describe 'Users', type: :request do
  describe 'profiles' do
    context 'should be visible by' do
      it 'related company managers' do
        company = create(:company)
        sign_in create(:manager, :company, company: company) # Pay attention on this
        get user_path(create(:member, :company, company: company)) # this
        expect(response).to have_http_status 200
      end

      it 'related company owners' do
        company = create(:company)
        sign_in create(:owner, :company, company: company) # this
        get user_path(create(:member, :company, company: company)) # and this
        expect(response).to have_http_status 200
      end
    end
  end
end

在用户规范下只有63个示例,但这两个示例就足够了。我想重构代码以使用let,它将使用如下参数定义方法:

RSpec.describe 'Users', type: :request do
  let(:member) do |entity_name = :company, entity = create(entity_name)|
    create :member, entity_name, entity_name => entity
  end
  let(:manager) do |entity_name = :company, entity = create(entity_name)|
    create :manager, entity_name, entity_name => entity
  end
  let(:owner) do |entity_name = :company, entity = create(entity_name)|
    create :owner, entity_name, entity_name => entity
  end

  describe 'profiles' do
    context 'should be visible by' do
      it 'related company managers' do
        company = create(:company)
        sign_in manager(:company, company) # Become more readable here
        get user_path(member(:company, company)) # here
        expect(response).to have_http_status 200
      end

      it 'related company owners' do
        company = create(:company)
        sign_in owner(:company, company) # here
        get user_path(member(:company, company)) # and here.
        expect(response).to have_http_status 200
      end
    end
  end
end

在重构之后,守卫说:

  1) Users profiles should be visible by related company managers
     Failure/Error: sign_in manager(:company, company) # Become more readable here

     ArgumentError:
       wrong number of arguments (given 2, expected 0)
     # ./spec/requests/users_spec.rb:38:in `block (4 levels) in <top (required)>'

在Rspec核心的memorized_helpers.rb中,我看到了:

def let(name, &block)
  # We have to pass the block directly to `define_method` to
  # allow it to use method constructs like `super` and `return`.
  raise "#let or #subject called without a block" if block.nil?
  raise(
    "#let or #subject called with a reserved name #initialize"
  ) if :initialize == name
  MemoizedHelpers.module_for(self).__send__(:define_method, name, &block)

  # Apply the memoization. The method has been defined in an ancestor
  # module so we can use `super` here to get the value.
  if block.arity == 1
    define_method(name) { __memoized.fetch_or_store(name) { super(RSpec.current_example, &nil) } }
  else
    define_method(name) { __memoized.fetch_or_store(name) { super(&nil) } }
  end
end

似乎let的块应使用参数定义。或不? 我没有足够的经验来确定它,我很乐意找到它。

3 个答案:

答案 0 :(得分:0)

我认为您想要类似的东西

RSpec.describe 'Users', type: :request do
  describe 'profiles' do
    let(:request) { get user_path(user) }
    let(:user)    { create(:member, :company, company: company) }
    let(:company) { create(:company) }

    shared_examples_for 'has visibility to user' do
      before { sign_in employee }

      it do 
        request
        expect(response).to have_http_status(:success)
      end
    end

    context 'manager' do
      let(:employee) { create(:manager, company: company) }

      it_behaves_like 'has visibility to user'
    end

    context 'owner' do
      let(:employee) { create(:owner, company: company) }

      it_behaves_like 'has visibility to user'
    end
  end
end

类似地,您应该可以执行以下操作:

it_behaves_like 'has visibility to user' do
 let(:employee) { create(:manager, company: company) }
end

shared examples。我建议签出http://www.betterspecs.org/

(此外,由于:member请求是针对:user的,所以有一个名为GET而不是user_path的工厂是令人困惑的;我建议重命名。)

答案 1 :(得分:-1)

我找到了可能的解决方案:返回一个lambda并就地调用它

RSpec.describe 'Users', type: :request do
  let(:member) do
    ->(entity_name = :company, entity = create(entity_name)) do
      create :member, entity_name, entity_name => entity
    end
  end
  let(:manager) do
    ->(entity_name = :company, entity = create(entity_name)) do
      create :manager, entity_name, entity_name => entity
    end
  end
  let(:owner) do
    ->(entity_name = :company, entity = create(entity_name)) do
      create :owner, entity_name, entity_name => entity
    end
  end

  describe 'profiles' do
    context 'should be visible by' do
      it 'related company managers' do
        company = create(:company)
        sign_in manager.(:company, company) # Pay attention on the dot here
        get user_path(member.(:company, company)) # here
        expect(response).to have_http_status 200
      end

      it 'related company owners' do
        company = create(:company)
        sign_in owner.(:company, company) # here
        get user_path(member.(:company, company)) # and here.
        expect(response).to have_http_status 200
      end
    end
  end
end

这不完全是我想要的,但是看起来像。

答案 2 :(得分:-1)

另一个解决方案是创建助手。例如,在spec/support/users_helper.rb中创建一个文件。

module UsersSpecHelper

  def member(entity = :company)
    create :member, *entity(entity)
  end

  def manager(entity = :company)
    create :manager, *entity(entity)
  end

  def owner(entity = :company)
    create :owner, *entity(entity)
  end

  private

    def entity(entity)
      if entity.is_a? Symbol
        [entity, entity => create(entity)]
      else
        name = entity.class.to_s.downcase.to_sym
        [name, name => entity]
      end
    end
end

RSpec.configure do |config|
  config.include UsersSpecHelper, type: :request
end

spec/rails_helper.rb行中取消注释:

Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

然后可以更好地使用它:

RSpec.describe 'Users', type: :request do
  describe 'profiles' do
    context 'should be visible by' do
      it 'managers of related company' do
        company = create(:company)
        sign_in manager(company) # Changes are here
        get user_path(member(company)) # here
        expect(response).to have_http_status 200
      end

      it 'owners of related company' do
        company = create(:company)
        sign_in owner(company) # here
        get user_path(member(company)) # and here.
        expect(response).to have_http_status 200
      end
    end
  end
end