我已经开始使用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
的块应使用参数定义。或不?
我没有足够的经验来确定它,我很乐意找到它。
答案 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