我对GET请求的测试失败 - 问题在于ActiveRecord的时间戳在响应中的格式。 RSpec测试期望数据格式为Mon, 29 Dec 2014 13:37:09 UTC +00:00,
,但它接收2014-12-29T13:37:09Z
。有没有办法改变ActiveRecord时间戳的格式,或RSpec测试期望的内容?
修改
这是失败的测试:
describe 'GET /v1/tasks/:id' do
it 'returns an task by :id' do
task = create(:task)
get "/v1/tasks/#{task.id}"
expect(response_json).to eq(
{
'id' => task.id,
'title' => task.title,
'note' => task.note,
'due_date' => task.due_date,
'created_at' => task.created_at,
'updated_at' => task.updated_at
}
)
end
end
created_at
和updated_at
上的测试失败。 response_json
是一个解析JSON响应的辅助方法。
这是测试失败的地方:
expected: {"id"=>1, "title"=>"MyString", "note"=>"MyText", "due_date"=>Mon, 29 Dec 2014, "created_at"=>Mon, 29 Dec 2014 13:37:09 UTC +00:00, "updated_at"=>Mon, 29 Dec 2014 13:37:09 UTC +00:00}
got: {"id"=>1, "title"=>"MyString", "note"=>"MyText", "due_date"=>"2014-12-29", "created_at"=>"2014-12-29T13:37:09Z", "updated_at"=>"2014-12-29T13:37:09Z"}
答案 0 :(得分:3)
解决方案很简单 - 我已指定使用to_formatted_s()
方法使用ISO 8601格式化日期。以下是正确的传递版本:
require 'spec_helper'
describe 'GET /v1/tasks/:id' do
it 'returns an task by :id' do
task = create(:task)
get "/v1/tasks/#{task.id}"
expect(response_json).to eq(
{
'id' => task.id,
'title' => task.title,
'note' => task.note,
'due_date' => task.due_date.to_formatted_s(:iso8601),
'created_at' => task.created_at.to_formatted_s(:iso8601),
'updated_at' => task.updated_at.to_formatted_s(:iso8601)
}
)
end
end
答案 1 :(得分:2)
问题是日期列是以您可能指定或未指定的格式进行“jsonified”的。该值具有不同的格式和字符串;因此将其与日期值匹配失败。
要解决此问题,您可以调整created_at
值'jsonified'的格式(格式化),或者您可以在测试中执行此操作:
columns = %w{id title note due_date created_at updated_at}
# get task as json string:
task_json_string = task.to_json(only: columns)
# turn json into hash
task_json = ActiveSupport::JSON.decode(task_json_string)
json_attributes = task_json.values.first
expected_hash = columns.each_with_object({}) do |attr, hash|
# ensuring that the order of keys is as specified by 'columns'
hash[attr] = json_attributes[attr]
end
expect(response_json).to eq expected_hash
这应该可行(尽管可能需要进行小调试)。
要转到以前的路线,请将以下类似的实例方法添加到Task
模型中:
def as_json( *args )
super(*args).tap do |hash|
attributes_hash = hash.values.first
%w{created_at updated_at}.each do |attr|
if attributes_hash.has_key?(attr)
# Use strftime time instead of to_s to specify a format:
attributes_hash[attr] = self.send(attr).to_s
end
end
end
end
然后在你的测试中你的task
json可能只是:
task_json = task.as_json.values.first
或者在添加add_json
方法并保留to_s
之后,在测试中,您需要做的只是在日期方法中添加.to_s
,例如task.created_at.to_s
;值将匹配。
从而删除object-to-json-to-hash步骤并直接执行object-to-hash。
答案 2 :(得分:2)
以下日期和日期时间格式应该可以解决问题。
{
'id' => task.id,
'title' => task.title,
'note' => task.note,
'due_date' => task.due_date.strftime("%Y-%m-%d %H:%M:%S %Z"),
'created_at' =>task.created_at.strftime("%Y-%m-%d %H:%M:%S %Z"),
'updated_at' =>task.updated_at.strftime("%Y-%m-%d %H:%M:%S %Z")
}
修改强> 更正日期格式。
答案 3 :(得分:0)
正如@Humza所提到的那样,问题源于价值如何被“jsonified”。
tl; dr - 只需将您的规范更改为'created_at' => task.created_at.as_json
在ActiveSupport(5.1.4)中,所有值都通过ActiveSupport::JSON::Encoding#jsonify
运行:
def jsonify(value)
case value
when String
EscapedString.new(value)
when Numeric, NilClass, TrueClass, FalseClass
value.as_json
when Hash
Hash[value.map { |k, v| [jsonify(k), jsonify(v)] }]
when Array
value.map { |v| jsonify(v) }
else
jsonify value.as_json
end
end
正如您所看到的,ActiveSupport::TimeWithZone
没有特殊情况,它只是调用ActiveSupport::TimeWithZone#as_json
。那么让我们看看它是什么样的:
[1] pry(#<DataModelRowSerializer>)> show-source ActiveSupport::TimeWithZone#as_json
From: /Users/stevehull/.rbenv/versions/2.4.2/lib/ruby/gems/2.4.0/gems/activesupport-5.1.4/lib/active_support/time_with_zone.rb @ line 165:
Owner: ActiveSupport::TimeWithZone
Visibility: public
Number of lines: 7
def as_json(options = nil)
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
xmlschema(ActiveSupport::JSON::Encoding.time_precision)
else
%(#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
end
end
因此,如果您愿意,可以期待'created_at' => task.created_at.xmlschema(ActiveSupport::JSON::Encoding.time_precision)
,但使用as_json
会更加紧凑;)