Ruby 在RSpec中,如何期望头包含在可接受值列表中?
我正在尝试编写一个规范,该规范断言来自API调用的HTTP头都包含在可接受头的列表中(也包含可接受的值) 我最后写了这样的东西:Ruby 在RSpec中,如何期望头包含在可接受值列表中?,ruby,unit-testing,rspec,Ruby,Unit Testing,Rspec,我正在尝试编写一个规范,该规范断言来自API调用的HTTP头都包含在可接受头的列表中(也包含可接受的值) 我最后写了这样的东西: expect(response.headers).to all(be_included_in(acceptable_headers)) 其中,be\u include\u in是一个自定义匹配器: RSpec::Matchers.define :be_included_in do |enumerable| match do |element| enume
expect(response.headers).to all(be_included_in(acceptable_headers))
其中,be\u include\u in
是一个自定义匹配器:
RSpec::Matchers.define :be_included_in do |enumerable|
match do |element|
enumerable.include?(element)
end
end
这对于断言头都在包含的范围内非常有效,但不满足测试其值以供接受的要求
你知道如何优雅地做到这一点吗?事实证明,通过现有的匹配器、否定匹配器和一点存在逻辑魔法,这是可能的 以下是组件: 否定匹配器:
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
规范“它只返回允许的标题”。这里的逻辑学家现在知道,这可以重写为“它不返回未包含在允许的头中的头”。就这样:
it 'includes only allowed headers' do
expect(some_result).to match(
a_hash_not_including(
headers: a_hash_not_including(acceptable_headers)
)
)
end
这里有一个解决方案,它将您最初尝试的风格与根据Header Name=>RSpec matcher的散列检查实际头的想法结合起来。它实现了以下功能:
- 从
调用中的响应中获取头文件可以使匹配器保持简单,并使其完全与头文件有关,因为每个人都知道HTTP,所以很容易考虑头文件expect()
- 它不使用否定匹配器,这比使用多个否定的解决方案更容易思考
- 它处理了两个双负解不能处理的情况,我将在下面描述
# 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
如果头:
键值对丢失,则您的双负解决方案将通过,我怀疑这不应该发生,尽管这可能永远不会发生。如果在nil
上调用此匹配器,则会引发NoMethodError
,如果不尽可能友好,这可能是正确的。同样,主要的一点是,最好的回答不是匹配者的问题
此匹配器还处理两种双负解决方案无法处理的情况:
- 空标头哈希应传递:
expect({}).to all_be_acceptable_headers
- RSpec的
有一个令人惊讶的行为(我在找出您的解决方案似乎不太正确的原因时发现了这一点):在include
被视为include
,因此上述操作失败。但是在include\u all\u of
expect([0]).not_to include(0, 1)
被视为include
,因此上述操作也失败 因此,如果有多个可接受的头,并且实际的头哈希有一个可接受的头和一个不可接受的头,则双负解决方案将通过。此匹配器处理以下内容:include\u any\u of
expect({ 'Content-Type' => "not a content type", 'Content-Length' => "123" }). not_to all_be_acceptable_headers
代码>在您的匹配器中输入错误?接受的值非常简单,可以用RSpec匹配器表示(尽管也有用于复杂条件的RSpec匹配器…)。事实上,我想出了一个解决方案,我会将其作为一个答案发布,以获得反馈。你说得对,匹配器中的代码>是一个输入错误!如果可接受标头列表中的标头不存在(与具有不正确的值相反),测试是否应通过?是的,其想法是“仅”返回可接受的标头。也就是说包含?(header)=>可接受(header)
,因此对于不包含?(header)或可接受(header)
,这是正确的。
expect({ 'Content-Type' => "not a content type", 'Content-Length' => "123" }).
not_to all_be_acceptable_headers