我试图创建帮助器,以统一和良好的方式测试JSON响应。
对于更多描述性和特定的故障,我希望每个JSON原子生成一个示例。
示例语法:
RSpec.describe "some JSON API View" do
setup_objects
before { render }
describe "response" do
subject { rendered }
it_conforms_to_json(
id: 27,
email: "john.smith@example.com",
name: "John",
profile_description: %r{professional specialist},
nested: {
id: 37,
status: "rejected"
}
)
end
end因此,这个片段将等价于:
RSpec.describe "some JSON API View" do
setup_objects
before { render }
describe "response" do
subject { rendered }
it 'has equal value at object["id"]' do
expect(subject["id"]).to eq(27)
end
it 'has equal value at object["email"]' do
expect(subject["email"]).to eq("john.smith@example.com")
end
it 'has equal value at object["name"]' do
expect(subject["name"]).to eq("John")
end
it 'has matching value at object["profile_description"]' do
expect(subject["profile_description"]).to match(%r{professional specialist})
end
it 'has equal value at object["nested"]["id"]' do
expect(subject["nested"]["id"]).to eq(37)
end
it 'has equal value at object["nested"]["status"]' do
expect(subject["nested"]["status"]).to eq("rejected")
end
end
end通过这段代码,我可以轻松地做到这一点:
module JsonHelper
extend self
def it_conforms_to_json(json)
generate_examples_for(json)
end
private
def generate_examples_for(json, opts)
with_prefix = opts.fetch(:with_prefix, [])
if json.is_a?(Hash)
json.each do |key, new_json|
new_prefix = with_prefix + [key.to_s]
generate_examples_for(new_json, opts.merge(with_prefix: new_prefix))
end
elsif json.is_a?(Array)
raise NotImplemented.new("Arrays are not allowed yet")
elsif json.is_a?(String) || json.is_a?(Numeric)
it "is expected to have equal value at json[\"#{with_prefix.join('"]["')}\"]" do
value = JSON.parse(subject)
with_prefix.each { |key| value = value[key.to_s] }
expect(value).to eq(json)
end
end
end
end只需通过扩展它来启用它:rspec_config.extend JsonHelper
当你想到相当失败的信息时,明显的问题就开始了:
it "is expected...bla-bla-bla的位置it_conforms_to_json(...)呼叫的位置。首先,通过添加回退跟踪排除/清除模式是可以修复的,但是它会导致一个完整的回溯跟踪,因为所有内容都是经过过滤的。
第二个和以前的版本可以通过在expect中包装begin..rescue语句来修复,通过使用file_path:line of it_conforms_to_json(...)调用预先加上file_path:lineofit_conforms_to_json(...)调用和重新引发修改过的异常来用回溯来破坏。
在前两个问题得到解决后,出现了新的问题:
怀疑的方法是find_first_non_rspec_line (或类似的方法),它通过应用默认的rspec排除regex‘’es在it调用的回溯跟踪(而不是异常回溯)中找到第一行。
它可以通过损坏RSpec内部部件来修复,即:
LIB_REGEX并再次定义它,将此助手文件添加到此正则表达式中IGNORE_REGEX这个补丁的代码变得糟糕和不可读,它将是地狱的维护,因为它依赖于rspec内部实现,这可以改变从小/补丁版本到版本。谁有兴趣读这个丑陋的东西这里
更多的是,对于rspec 2和rspec 3,它的工作方式不同。
rspec 2,完美的要求:
Failures:
1) A json response is expected to have equal value at json["id"]
Failure/Error: it_conforms_to_json(
expected: 37
got: 25
(compared using ==)
# ./spec/simple_with_fail_spec.rb:10:in `block in <top (required)>'
2) A json response is expected to have equal value at json["name"]
Failure/Error: it_conforms_to_json(
expected: "Smith"
got: "John"
(compared using ==)
# ./spec/simple_with_fail_spec.rb:10:in `block in <top (required)>'rspec 3,略为偏离,但仍可接受:
失败:
1) A json response is expected to have equal value at json["id"]
Failure/Error: expect(value).to eq(json)
expected: 37
got: 25
(compared using ==)
# ./spec/simple_with_fail_spec.rb:10:in `block in <top (required)>'
# ./lib/rspec/json_expectations/expectations.rb:32:in `block in generate_examples_for'
2) A json response is expected to have equal value at json["name"]
Failure/Error: expect(value).to eq(json)
expected: "Smith"
got: "John"
(compared using ==)
# ./spec/simple_with_fail_spec.rb:10:in `block in <top (required)>'
# ./lib/rspec/json_expectations/expectations.rb:32:in `block in generate_examples_for'以下是一些问题:
发布于 2014-08-18 20:31:37
因此,它现在有点难看(虽然是第一次迭代),但是很有效。不使用RSpec内部组件,只使用公共RSpec API,不使用很好的错误消息,甚至无需清除回溯跟踪:
module RSpec
module JsonExpectations
class JsonTraverser
def self.traverse(errors, expected, actual, prefix=[])
if expected.is_a?(Hash)
expected.map do |key, value|
new_prefix = prefix + [key]
if actual.has_key?("#{key}")
traverse(errors, value, actual["#{key}"], new_prefix)
else
errors[new_prefix.join("/")] = :no_key
false
end
end.all?
elsif expected.is_a?(String) || expected.is_a?(Numeric)
if actual == expected
true
else
errors[prefix.join("/")] = {
actual: actual,
expected: expected
}
false
end
else
raise NotImplementedError, "#{expected} expectation is not supported"
end
end
end
end
end
RSpec::Matchers.define :include_json do |expected|
match do |actual|
unless expected.is_a?(Hash)
raise ArgumentError, "Expected value must be a json for include_json matcher"
end
RSpec::JsonExpectations::JsonTraverser.traverse(
@include_json_errors = {},
expected,
JSON.parse(actual)
)
end
# RSpec 2 vs 3
send(respond_to?(:failure_message) ?
:failure_message :
:failure_message_for_should) do |actual|
res = []
@include_json_errors.each do |json_path, error|
res << %{
json atom on path "#{json_path}" is missing
} if error == :no_key
res << %{
json atom on path "#{json_path}" is not equal to expected value:
expected: #{error[:expected].inspect}
got: #{error[:actual].inspect}
} if error.is_a?(Hash) && error.has_key?(:expected)
end
res.join("")
end
end它现在允许这样的语法:
it "has basic info about user" do
expect(subject).to include_json(
id: 37,
email: "john.smith@example.com",
name: "Smith"
)
end它会生成这样的错误:
F
Failures:
1) A json response has basic info about user
Failure/Error: expect(subject).to include_json(
json atom on path "id" is not equal to expected value:
expected: 37
got: 25
json atom on path "name" is not equal to expected value:
expected: "Smith"
got: "John"
# ./spec/simple_with_fail_spec.rb:11:in `block (2 levels) in <top (required)>'
Finished in 0.00065 seconds
1 example, 1 failure
Failed examples:
rspec ./spec/simple_with_fail_spec.rb:10 # A json response has basic info about user同时适用于RSpec 2和3。
如果有人感兴趣,就在这里,期望值
https://stackoverflow.com/questions/25192075
复制相似问题