Rails のテストと Minitest を学習していて、refute
と assert_not
は同じものなのか違うものなのか気になったので調べてみた。
アサーション
アサーション(assertion)とは、オブジェクトや式を評価して、期待された結果が得られるかどうかをチェックするコードのこと。
アサーション | 目的 |
---|---|
assert A | A が true であることを検証する |
assert_equal A(期待値), B(実際の値) | A == B であることを検証する |
assert_not A | A が false であることを検証する |
assert_not_equal A B | A != B であることを検証する |
assert_empty A | A が空(empty?)であることを検証する |
2.6 利用可能なアサーション Rails テスティングガイド - Railsガイド
ここで疑問が。チェリー本(プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで Software Design plus)で Minitest を勉強したとき、assert
の反対はrefute
だったはず。
assert の反対は refute では?
Rails コーディングルールはこうなっているようだ。
refuteではなくassert_notを使用すること。
Ruby on Rails に貢献する方法 - Railsガイド
実際にテストを書いてみると、Rubocop に refute_equal
ではなく assert_not_equal
の方がいいよ!と指摘された。
refute
と assert_not
にどういう違いがあるのか気になって Minitest のドキュメントを見たら、やはりfalse
を確認するのはrefute
となっていて、assert_not
がつくものを見つけることができなかった。
Module: Minitest::Assertions — Documentation for minitest (5.14.1)
上のテストは Minitest で書いていたので、 assert_not_equal
に書き換えると以下のようなエラーが出た。
Error: FizzbuzzTest#test_15を渡すと文字列Buzzを返さない: NoMethodError: undefined method `assert_not_equal' for #<FizzbuzzTest:0x00007fd79e2286e8> Did you mean? assert_equal fizzbuzz_minitest.rb:26:in `test_15を渡すと文字列Buzzを返さない'
まとめると、
- Rails コーディングルール:
refute
ではなくassert_not
を使う - Rubocop:
refute
ではなくassert_not
を使う - Minitest:
refute
を使う。assert_not
はない
以上のことから、assert_not
系は Minitest にはないもので、Rails のテストにはある、ということがわかった。Rails のテストってなんだっけ?
Rails 標準のテストクラスは、ActiveSupport::TestCase
を継承している。ActiveSupport::TestCase
は、Ruby 標準のテストであるMinitest::Test
を継承している。
つまり、ActiveSupport::TestCase
の独自拡張部分か!
ActiveSupport::Testing::Assertions
コードを見比べる
ということで、やっとたどり着いた。"active_support/testing/assertions" で定義されていた。
assert_not
のコード
def assert_not(object, message = nil) message ||= "Expected #{mu_pp(object)} to be nil or false" assert !object, message end
rails/assertions.rb at 77932446895bd70f38a3aaa1ab288bf8a7b7142c · rails/rails · GitHub
refute
のコード(Minitest)
def refute test, msg = nil msg ||= message { "Expected #{mu_pp(test)} to not be truthy" } assert !test, msg end
minitest/assertions.rb at master · seattlerb/minitest · GitHub
コードから、refute
もassert_not
も同じと考えてよさそうだ。
assert_not_equal
などはエイリアスとして設定されてるっぽい。なんでassert_not
はエイリアスじゃだめなんだろう。
余談
Rails じゃないけど、なんとかして assert_not
を使ってみたい。特に意味はないけど…。試行錯誤の結果、Rails のテストと同じようにActiveSupport::TestCase
クラスを継承したテストクラスを作ったら、できた!
require "minitest/autorun" require "active_support/all" require "./fizzbuzz" class FizzbuzzTest < ActiveSupport::TestCase def setup @fb = Fizzbuzz.new end def test_15を渡すと文字列Buzzを返さない assert_not_equal "Buzz", @fb.fizzbuzz(15) end end
https://github.com/masuyama13/fizzbuzz/blob/master/fizzbuzz_active_support_test.rb
わーい
ところで、試行錯誤中いろいろツイートしてたら、Rubocop コミッターの方にリプライいただきちょっと感動。
はい。`Rails/RefuteMethods` cop は、Rails リポジトリでのコーディング規約を元にした `assert_not` への統一がデフォルトです。
— Koichi ITO (@koic) 2020年8月6日
> Use assert_not methods instead of refute.https://t.co/HMnjAYz1vR
スタイルの好みの部分でもあるので、`refute` に矯正するオルタナティブの設定もあります。