When to_json
is called on an object in Rails, the datetime
objects are converted to the ISO 8601 standard automatically which is an excellent tool for sending data back to a client.
[43] pry(main)> { first_name: 'Arslan', date_of_birth: user.created_at }.to_json
=> "{\"first_name\":\"Arslan\",\"date_of_birth\":\"2024-08-21T19:44:17.423Z\"}"
[44] pry(main)> user.created_at
=> Wed, 21 Aug 2024 19:44:17.423106000 UTC +00:00
[45] pry(main)>
However in RSpec or Minitest, one has to manually convert the time, like the following:
expect(parsed_body).to eq({ pin_code_sent_at: user.pin_code_sent_at.utc.iso8601(3)}.stringify_keys)
Just like it's automatic in to_json
, is there a way to accomplish a similar in RSpec or Minitest?
One hacky way I could find:
expect(parsed_body).to eq(JSON.parse({ pin_code_sent_at: user.pin_code_sent_at).to_json)
Of course, it involves converting a hash to JSON, and parsing it back, just to have the time format right & comparable.
You're facing this problem because JSON.parse
won't re-create the time object:
t = Time.parse('2024-08-30T14:30+0000')
data = { key: t }
#=> {:key=>2024-08-30 14:30:00 +0000}
json = data.to_json
#=> "{\"key\":\"2024-08-30T14:30:00.000+00:00\"}"
parsed_body = JSON.parse(json)
#=> {"key=">"2024-08-30T14:30:00.000+00:00"
As you can see, the value for :key
/ "key"
changed from an instance of Time
to an instance of String
. This behavior is expected, from the JSON
docs:
When you “round trip” a non-String object from Ruby to JSON and back, you have a new String, instead of the object you began with
The documentation then explains so-called JSON additions which add class specific details to the JSON string for parsing. But I guess this isn't an option as it will significantly alter the generated JSON and only works for Ruby.
However, an easier way to get a comparable structure is to retrieve the time object's JSON representation via as_json
from ActiveSupport's JSON support. The corresponding key can be given as a literal string:
expect(parsed_body).to eq({ "key" => t.as_json })