简体   繁体   English

RSpec + Rubocop - 为什么 receive_message_chain 是代码异味?

[英]RSpec + Rubocop - why receive_message_chain is a code smell?

I am about to write specs for my custom validator, that uses this chain to check if a file attach with ActiveStorage is a txt:我即将为我的自定义验证器编写规范,它使用此链来检查使用 ActiveStorage 附加的文件是否为 txt:

return if blob.filename.extension.match?('txt')

Normally, I would be able to stub it with this call:通常,我可以通过这个调用来存根:

allow(attached_file).to receive_message_chain(:blob, :byte_size) { file_size }

Rubocop says it is an offence and points me to docs: https://www.rubydoc.info/gems/rubocop-rspec/1.7.0/RuboCop/Cop/RSpec/MessageChain Rubocop 说这是一种冒犯,并指出我的文档: https ://www.rubydoc.info/gems/rubocop-rspec/1.7.0/RuboCop/Cop/RSpec/MessageChain

I would have to declare double for blob and byte_size and stub them in separate lines, ending up with 5 lines of code instead of 1. Am I missing something here?我必须为blobbyte_size声明 double 并将它们存根在单独的行中,最后得到 5 行代码而不是 1 行。我在这里遗漏了什么吗?

Why should I avoid stubbing message chains?为什么我应该避免存根消息链?

I would have to declare double for blob and byte_size and stub them in separate lines, ending up with 5 lines of code instead of 1.我必须为 blob 和 byte_size 声明 double 并将它们存根在单独的行中,最后得到 5 行代码而不是 1 行。

This is, in fact, the point.事实上,这就是重点。 Having those 5 lines there likely will make you feel slightly uneasy.有这 5 行可能会让你感到有点不安。 This can be thought of as positive design pressure .这可以被认为是正设计压力 Your test setup being complex is telling you to have a look at the implementation.您的测试设置很复杂,这告诉您查看实现。 Using #receive_message_chains allows us to feel good about designs that expose complex interactions up front.使用#receive_message_chains让我们对#receive_message_chains暴露复杂交互的设计感觉良好。

One of the authors of RSpec explains some of this in a GitHub issue . RSpec 的一位作者在GitHub 问题 中解释了其中的一些内容。

What can I do instead?我可以做什么?

One option is to attach a fixture file to the record in the setup phase of your test:一种选择是在测试的设置阶段将夹具文件附加到记录:

before do
  file_path = Rails.root.join("spec", "fixtures", "files", "text.txt")

  record.attribute.attach(io: File.open(file_path), filename: "text.txt")
end

This will test the validator end-to-end, without any stubbing.这将端到端地测试验证器,没有任何存根。


Another option is to extract a named method, and then stub that instead.另一种选择是提取一个命名方法,然后将其存根。

In your validator:在您的验证器中:

def allowed_file_extension?
  blob.filename.extension.match?("txt")
end

In your test:在您的测试中:

before do
  allow(validator).to receive(:allowed_file_extension?).and_return(true)
end

This has the added benefit of making the code a little clearer by naming a concept.这有一个额外的好处,即通过命名一个概念使代码更清晰一些。 (There's nothing preventing you from adding this method even if you use a test fixture.) (即使您使用测试装置,也不会阻止您添加此方法。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM