I have a failing test for a GET request — the issue lies with the way ActiveRecord's timestamps are formatted in the response. The RSpec test expects the data format to be Mon, 29 Dec 2014 13:37:09 UTC +00:00,, but it instead receives 2014-12-29T13:37:09Z. Is there any way to change the formatting of the ActiveRecord timestamp, or what the RSpec test expects?
Edit:
Here's the failing test:
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
The test fails on created_at and updated_at. response_json is a helper method that parses the JSON response.
Here's where the test fails:
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"}
The solution was embarrassing simple — I had specify that the dates should be formatted using ISO 8601 using the to_formatted_s() method. Below is the correct, passing version:
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
The problem is that the date columns are being 'jsonified' in a format that you may or may not have specified. This value is of a different format and a string; therefore matching it with a date value fails.
To resolve this you could either adjust how the created_at value is 'jsonified' (format it) or you could just do this in your test:
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
That should work (minor debugging might be required though).
To go the former route, add an instance method like the following to your Task model:
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
Then in your test your task json could simply be:
task_json = task.as_json.values.first
Or after adding the add_json method and keeping the to_s there, in your test all you will need to do is add a .to_s to your date method e.g. task.created_at.to_s; the values will match.
Thereby removing the object-to-json-to-hash step and doing object-to-hash directly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With