RSpec/Minitest能否像Rails to_json那样自动处理ISO 8601格式?
在RSpec/Minitest中实现to_json式的Datetime自动格式转换
在Rails中调用对象的to_json方法时,Datetime对象会自动转换为ISO 8601标准格式,这很适合给客户端返回数据,示例如下:
[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
但在RSpec或Minitest这类测试框架里,我们得手动转换时间格式才能和接口返回的parsed_body匹配,比如这样写:
expect(parsed_body).to eq({ pin_code_sent_at: user.pin_code_sent_at.utc.iso8601(3)}.stringify_keys)
实现自动转换的方法
1. 复用Rails序列化逻辑的"取巧"方案
直接利用Rails的to_json和JSON.parse来回转换,让时间自动套用框架内置的序列化规则,代码如下:
expect(parsed_body).to eq(JSON.parse({ pin_code_sent_at: user.pin_code_sent_at }.to_json))
这个方法完全复用Rails的时间格式化逻辑,无需手动调用utc.iso8601(3),能保证格式和接口返回完全一致。
2. 封装辅助方法(更高效)
如果频繁需要做这类匹配,可以封装一个辅助方法减少重复代码:
# RSpec:在spec/support/test_helpers.rb中添加 # Minitest:在test/test_helper.rb中添加 def serialize_for_response(hash) JSON.parse(hash.to_json) end
之后测试里就能简化成:
expect(parsed_body).to eq(serialize_for_response({ pin_code_sent_at: user.pin_code_sent_at }))
3. RSpec自定义匹配器(更语义化)
追求断言可读性的话,可以写一个自定义匹配器:
# spec/support/matchers.rb RSpec::Matchers.define :match_serialized do |expected_hash| match do |actual| actual == JSON.parse(expected_hash.to_json) end failure_message do |actual| "expected #{actual} to match serialized version of #{expected_hash}" end end
使用时直接写:
expect(parsed_body).to match_serialized({ pin_code_sent_at: user.pin_code_sent_at })
以上几种方式都能避免手动处理时间格式的繁琐,复用Rails的序列化逻辑,降低格式不匹配的出错概率。
内容的提问来源于stack exchange,提问作者Arslan Ali
相关产品推荐
相关产品推荐

