我正在尝试编写一个规范,声明来自API调用的HTTP头都包含在可接受的头文件列表中(同样具有可接受的值)。
我最后写了这样的东西:
expect(response.headers).to all(be_included_in(acceptable_headers))
其中be_included_in
是自定义匹配器:
RSpec::Matchers.define :be_included_in do |enumerable|
match do |element|
enumerable.include?(element)
end
end
这适用于声明标题都在包含范围内,但不满足测试其值以供接受的要求。
任何想法如何优雅地做到这一点?
答案 0 :(得分:2)
这是一个解决方案,它将您的初始尝试的风格与审核实际标题的方法相结合,以对象Hash of Header-Name => RSpec匹配器。它完成以下任务:
expect()
调用中的响应中获取标题可以使匹配器保持简单,并且可以将其全部用于标题,因为每个人都知道HTTP,所以很容易思考。这是匹配器:
# I changed the first acceptable header and added a second to test that
# the matcher handles multiple acceptable headers correctly
let(:acceptable_headers) do
{
'Content-Type' => match(/^[a-z\-_.]+\/[a-z\-_.]+$/),
'Content-Length' => match(/^\d+$/)
}
end
RSpec::Matchers.define :all_be_acceptable_headers do
match do |actual|
actual.all? do |actual_key, actual_value|
acceptable_headers.any? do |acceptable_key, acceptable_value|
actual_key == acceptable_key && acceptable_value.matches?(actual_value)
end
end
end
# This is better than the default message only in that it lists acceptable headers.
# An even better message would identify specific unacceptable headers.
failure_message do |actual|
"expected that #{actual} would match one of #{acceptable_headers}"
end
end
它处理这些双阴性解决方案也处理的例子:
expect({ 'Content-Type' => "application/xml" }).to all_be_acceptable_headers
expect({ 'Content-Type' => "application/xml", 'Content-Length' => "123" }).to all_be_acceptable_headers
expect({ 'Content-Tape' => "application/xml" }).not_to all_be_acceptable_headers
expect({ 'Content-Type' => "not a content type" }).not_to all_be_acceptable_headers
如果缺少headers:
键值对,你的双阴性解决方案会通过,我怀疑它不应该,但这可能永远不会发生。如果在NoMethodError
上调用此匹配器会引发nil
,如果不尽可能用户友好,则可能是正确的。同样,重点是让响应不是匹配器的问题更好。
这个匹配器还处理两种情况,你的双阴性解决方案没有:
空标题哈希应该通过:
expect({}).to all_be_acceptable_headers
RSpec的include
有一个令人惊讶的行为(我在发现你的解决方案看起来不太合适的时候发现了这一点):in
expect([0]).to include(0, 1)
include
被视为include_all_of
,因此上述操作失败。但是在
expect([0]).not_to include(0, 1)
include
被视为include_any_of
,因此上述内容也失败了!
因此,如果有多个可接受的标头,并且实际的标头散列有一个可接受的标头和一个不可接受的标头,则双负解决方案会通过。这个匹配器处理:
expect({ 'Content-Type' => "not a content type", 'Content-Length' => "123" }).
not_to all_be_acceptable_headers
答案 1 :(得分:0)
事实证明,现有的匹配器,否定的匹配器以及一些存在逻辑魔法都可以实现这一点。
以下是组件:
否定匹配者:
RSpec::Matchers.define_negated_matcher :does_not_include, :include
RSpec::Matchers.alias_matcher :a_hash_not_including, :does_not_include
接受标题:
let(:acceptable_headers) do
{
'Content-Type' => be_a(String)
}
end
Spec“它只返回允许的标题”。此处的逻辑工程师现在知道这可以重写为“它不会返回未包含在允许的标题中的标题”。它就是这样:
it 'includes only allowed headers' do
expect(some_result).to match(
a_hash_not_including(
headers: a_hash_not_including(acceptable_headers)
)
)
end