9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

datetime型のrspec書くときにミリ秒単位で値がずれてfailすることがある問題について

Last updated at Posted at 2016-11-22

前提

Rails 4.2系
MySQL 5.6.4以降

問題

同じ値を比較しているはずなのに、こんなことがおきる..!!

    expected: "2016-11-22T05:30:12.291Z"
         got: "2016-11-22T05:30:12.000Z"

ミリ秒..

結論(僕の場合は)

MySQLのdatetime系のカラムのミリ秒を設定しないまま、
createで作りたてのActiveRecordのインスタンスのdatetime型のattributeと
データベースからロードしてきたActiveRecordのインスタンスのdatetime型のattributeを比較しているのがfailしている原因。

Mysqlでのミリ秒の扱いについて

Mysql5.6.4以降ではdatetime系のカラムでミリ秒もサポートしている。
ただ、普通にmigrationを書くと、defaultではミリ秒は扱わないようになっていて、クエリにミリ秒が含まれていると、秒単位まで四捨五入して保存する。

なので、コードと一緒にかくと以下のようなことがおきる。

now = Time.zone.now # with ミリ秒
hoge = Hoge.new(fuga_datetime:  now) # fuga_datetimeはwith ミリ秒
hoge.save # ミリ秒は切り捨てて保存される
hoge.fuga_datetime # インスタンスにはnowがほじされているので with ミリ秒
Hoge.last.fuga_datetime # => DBからのloadなので、without ミリ秒。 式で書くとここでは、hoge.fuga_datetime != UserFood.last.fuga_datetime 

こんな感じ。

なんで、たとえば以下のようなget api の specはfailする。
get apiは(当然)データベースから読み込んだインスタンスを使ってレスポンスを返しているのに、
specで比較するときのインスタンスはレコード作成時のインスタンスを使っているため。

let(:hoge) do
  create(:hoge, fuga_datetime: Time.zone.now) # hoge.fuga_datetimeはミリ秒をもってしまう
end

subject do
  get "some/endpoint/hoges/#{hoge.id}"
end

it do
  subject
  body_hash = JSON.parse(response.body)
  expect(body_hash['hoge']['fuga_datetime']).to eq hoge.fuga_datetime.iso8601(3) # because fuga_datetime has milli-seconds
end

上記の場合は、単にhogeをreloadすれば成功します

it do
  subject
  hoge.reload # fuga_datetimeをデータベースの値(without ミリ秒)へ
  ...
end

to_iして比較しろ的なstack over flowがいくつかでてくるんですが、
ミリ秒はmysqlは四捨五入、to_iは切り捨てなのでミリ秒が0.5秒以上になるとfailするのでやっぱりだめ。

it do
  subject
  body_hash = JSON.parse(response.body)
  expect(body_hash['hoge']['fuga_datetime'].to_time.to_i).to eq hoge.to_i # たまに1ずれる
end

やっぱりreloadするのが良さそう。

まとめ

作りたてのインスタンスのdate time型のattributeをspecでかくときはreloadした上で使いましょう

参考

MySQLのミリ秒の扱いについて

https://doruby.jp/users/ap_tg/entries/MySQL_DATETIME_
https://dev.mysql.com/doc/refman/5.6/ja/fractional-seconds.html

Railsでのミリ秒の扱いについて(ちょっと古いけどさくっとまとまってる)

9
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?