9
2

【Ruby】そのassert_raisesの使い方、本当に合ってる? - assert_raisesでエラーメッセージをテストする方法

Posted at

はじめに

以下のテストで次の2点をテストしようとしています

  • 特定の条件でraiseが発生しているか
  • エラーメッセージが正しく出力されているか

このテストの何が悪いか皆さんは分かりますか?

sample_code.rb
class SampleCode
  def self.animal_check(animal)
    raise '猫はアレルギーだからダメ' if animal == 'cat'

    raise '犬は可愛すぎるからダメ' if animal == 'dog'

    pp '他の動物は許す'
  end
end
test.rb
  test '猫はアレルギーだからraise' do
    assert_raises(RuntimeError, '猫はアレルギーだからダメ') do
      SampleCode.animal_check('cat')
    end
  end

とあるプロジェクトのテストコードを眺めていたところ、陥りがちなあるミスを発見したので戒めとして記事にしようと思います。
では、なぜこのテストコードが悪いかについて解説していきます。

このテストの何がいけないのか?

答えはassert_raisesの使い方です。
実は以下のようにコードをいじってもこのテストは通ってしまいます。

test.rb
  test '猫はアレルギーだからraise' do
    assert_raises(RuntimeError, 'どんなエラーメッセージでも落ちないよ〜') do
      SampleCode.animal_check('cat')
    end
  end

assert_raisesの解説

assert_raisesが具体的にどんな挙動をするかを見ていきましょう。
assert関連のメソッドが定義されているassertions.rbには、以下のようにassert_raisesが定義されています。

assertions.rb

    def assert_raises *exp
      flunk "assert_raises requires a block to capture errors." unless
        block_given?

      # assert_raisesの最後の引数が文字列の場合は、それをテストの失敗メッセージとする。
      msg = "#{exp.pop}.\n" if String === exp.last
      exp << StandardError if exp.empty?

      begin
        yield
      rescue *exp => e
        pass
        return e
      rescue Minitest::Assertion
        raise
      rescue SignalException, SystemExit
        raise
      rescue Exception => e
        flunk proc {
          exception_details(e, "#{msg}#{mu_pp(exp)} exception expected, not")
        }
      end

      exp = exp.first if exp.size == 1

      flunk "#{msg}#{mu_pp(exp)} expected but nothing was raised."
    end

上記コードを見ながら、初めに出したテストコードの何が悪かったかを解説していきます。

test.rb
  test '猫はアレルギーだからraise' do
    assert_raises(RuntimeError, '猫はアレルギーだからダメ') do 
      SampleCode.animal_check('cat')
    end
  end

assertions.rbのコードを見れば、第二引数の「猫はアレルギーだからダメ」はRuntimeErrorが発生しなかった際にテストの失敗メッセージとして表示されるということがわかりますね。
つまり、このテストコードでやっているのは、RuntimeErrorのエラーメッセージとして「猫はアレルギーだからダメ」が出力されるテストではないのです。

実際にやっていることは以下の2つなのです。

  • RuntimeErrorになればテスト成功
  • テスト失敗時には「猫はアレルギーだからダメ」をテストの失敗メッセージとして出力する

どうすればエラーメッセージをテストできるの?

以下のようにすればエラーメッセージのテストが可能となります。

test.rb
test '猫の場合はraise' do
    exception = assert_raises(RuntimeError) do
      SampleCode.animal_check('cat')
    end
    assert_equal('猫はアレルギーだからダメ', exception.error_message)
end
9
2
0

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
2