LoginSignup
45
36

More than 3 years have passed since last update.

Minitest::Assersionsのassert系メソッドを全部試してみた

Last updated at Posted at 2019-12-21

はじめに

この記事は Ruby Advent Calendar 2019 21日目の記事です。
今回はRubyのテストフレームワークであるMinitestのassert系メソッドを全部試してみました。
この記事がMinitestを使っている人に少しでも役立てばいいなと思います。
環境は以下のとおりです。

Ruby

ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-linux]

Minitest

Minitest::VERSION #=> "5.13.0"

それでは頑張っていきましょう。

assert(test, msg = nil)

testが真(nilfalse以外)の場合にアサーションが成功します。
msgを指定するとアサーション失敗時に指定したメッセージを表示します。
(これ以降紹介するメソッドでもmsgを引数に取れるメソッドがありますが、すべて同じ機能のため、ここでのみ紹介します。)

成功例

assert true
assert 0
assert :sym
assert "hello"

失敗例

assert false
Expected false to be truthy.

失敗例(メッセージあり)

assert nil, "nilだよ"
nilだよ

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert

assert_empty(obj, msg = nil)

obj.empty?が真の場合にアサーションが成功します。
empty?を実装していないオブジェクトを渡した場合はメッセージが表示され、アサーションに失敗します。

成功例

assert_empty({}) # {}がブロックと判定されてしまうため、()で括る
assert_empty []
assert_empty ""

失敗例

assert_empty({name: "TomoProg"})
assert_empty [1, 2, 3]
Expected {:name=>"TomoProg"} to be empty.
Expected [1, 2, 3] to be empty.

失敗例(empty?を実装していない)

assert_empty 0
Expected 0 (Integer) to respond to #empty?.

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_empty

assert_equal(exp, act, msg = nil)

exp == actが真の場合にアサーションが成功します。
Minitestのバージョン6からはexpnilを指定するとアサーションが失敗するようになるようです。
そのため、expnilを指定した場合、assert_nilを使うように警告が出ます。

成功例

assert_equal 1, 1
assert_equal "sample", "sample"

失敗例

assert_equal 2, 3
Expected: 2
  Actual: 3

expにnilを指定した場合

assert_equal nil, nil
DEPRECATED: Use assert_nil if expecting nil from test/sample_test.rb:5.
This will fail in Minitest 6.

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_equal

assert_in_delta(exp, act, delta = 0.001, msg = nil)

expactの差分((exp - act).abs)がdelta以下の場合にアサーションに成功します。
浮動小数点数の比較の際に誤差を許容する場合に使うようです。

成功例

assert_in_delta 0.001, 0.002
assert_in_delta 0.3, (0.1 + 0.2), 0.000001

失敗例

assert_in_delta 1.256, 1.255
Expected |1.256 - 1.255| (0.001000000000000112) to be <= 0.001.

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_in_delta

assert_in_epsilon(exp, act, epsilon = 0.001, msg = nil)

誤差に関しての理解不足もあり、このメソッドに関してはよく分かりませんでした。
assert_in_deltaと同じように浮動小数点数の誤差を許容する場合に使うようですが、
assert_in_delta絶対誤差を許容するのに対し、こちらは相対誤差を許容するようです。

参考になりそうなリンクを貼っておきます。
Minitestで絶対誤差と相対誤差を利用したテストを実行する

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_in_epsilon

assert_includes(collection, obj, msg = nil)

collection.include?(obj)が真の場合にアサーションに成功します。
include?(obj)を実装していないオブジェクトを渡した場合はメッセージが表示され、アサーションに失敗します。

成功例

assert_includes [1, 2, 3], 2
assert_includes({name: "TomoProg"}, :name) # Hash#include?(key)はハッシュにkeyがあれば真となる

失敗例

assert_includes [1, 2, 3], 0
Expected [1, 2, 3] to include 0.

失敗例(include?(obj)を実装していない)

assert_includes 1, 1
Expected 1 (Integer) to respond to #include?.

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_includes

assert_instance_of(cls, obj, msg = nil)

obj.instance_of?(cls)が真の場合にアサーションに成功します。

成功例

assert_instance_of Array, [1, 2, 3]
assert_instance_of Integer, 1

失敗例

assert_instance_of Integer, 1.2
Expected 1.2 to be an instance of Integer, not Float.

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_instance_of

assert_kind_of(cls, obj, msg = nil)

obj.kind_of?(cls)が真の場合にアサーションに成功します。
assert_instance_ofに似ていますが、assert_kind_ofはサブクラスでもアサーションに成功します。

成功例

class Base; end
class SuperClass < Base; end

assert_kind_of Base, SuperClass.new
assert_kind_of SuperClass, SuperClass.new

失敗例

assert_kind_of Integer, 1.2
Expected 1.2 to be a kind of Integer, not Float.

assert_instance_ofとの違い

class Base; end
class SuperClass < Base; end

assert_instance_of Base, SuperClass.new # assert_instance_ofではサブクラスを指定すると失敗する
assert_kind_of     Base, SuperClass.new # assert_kind_ofではサブクラスでも成功する

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_kind_of

assert_match(matcher, obj, msg = nil)

matcher =~ objが真の場合にアサーションに成功します。

成功例

assert_match /rin/, "string"
assert_match /\d{3}-\d{4}-\d{3}/, "123-4567-890"

失敗例

assert_match /rig/, "string"
Expected /rig/ to match # encoding: UTF-8
#    valid: true
"string".

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_match

assert_mock(mock)

mock.verifyが真の場合にアサーションに成功します。
Minitestではメソッド呼び出しが正しく行われているかを確認するために、モックを作ることができます。
verifyはモックに定義したメソッドがすべて呼び出されていればtrueを返し、呼び出されていないメソッドがあればMockExpectationErrorを返します。

サンプルクラス

class Sample
  def initialize(obj)
    @obj = obj
  end

  def call
    @obj.method1
  end
end

成功例

# モックにメソッドを定義する
# (第1引数にメソッド名、第2引数に戻り値を指定する)
mock = Minitest::Mock.new
mock.expect "method1", true

# Sample#callを呼び出す
Sample.new(mock).call

# モックに定義したすべてのメソッドが呼ばれたため、このアサーションは成功する
assert_mock mock

失敗例

# モックにメソッドを定義する
# (第1引数にメソッド名、第2引数に戻り値を指定する)
mock = Minitest::Mock.new
mock.expect "method1", true
mock.expect "method2", true

# Sample#callを呼び出す
Sample.new(mock).call

# method2が呼ばれていないため、このアサーションは失敗する
assert_mock mock
MockExpectationError: expected method2() => true

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_mock

assert_nil(obj, msg = nil)

obj.nil?が真の場合にアサーションに成功します。

成功例

assert_nil nil

失敗例

assert_nil 1
Expected 1 to be nil.

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_nil

assert_operator(o1, op, o2 = UNDEFINED, msg = nil)

o1.__send__(op, o2)が真の場合にアサーションに成功します。
2項演算子をテストするためのアサーションです。
o2UNDEFINEDの場合は後述するassert_predicateが呼び出されます。
UNDEFINED定数はMinitest::Assersionsで定義されており、Object.newが代入されていました。

成功例

assert_operator 5, :<=, 6

失敗例

assert_operator 5, :<=, 4
Expected 5 to be <= 4.

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_operator

assert_output(stdout = nil, stderr = nil) { || ... }

標準出力がstdout、標準エラー出力がstderrの場合にアサーションに成功します。
ブロックに標準出力、標準エラー出力を扱う処理を記述して使います。
正規表現を渡すことも可能です。
また、nilを指定した場合はテストされません。

成功例

# 標準出力、標準エラー出力をテストする
assert_output("sample\n", "stderr_sample\n") do
  puts "sample"
  warn "stderr_sample" # Kernel.#warnは指定された文字列を標準エラー出力に出力する
end

# 正規表現も指定できる
assert_output(/str/) do
  puts "string"
end

# nilを指定した場合はテストされない
assert_output(nil, "stderr_sample\n") do
  warn "stderr_sample"
end

失敗例

assert_output("sample\n") do
  puts "saaaample"
end
In stdout.
--- expected
+++ actual
@@ -1,4 +1,2 @@
-# encoding: UTF-8
-#    valid: true
-"sample
+"saaaample
 "

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_output

assert_path_exists(path, msg = nil)

File.exist?(path)が真の場合にアサーションに成功します。

ディレクトリ構成

sample/
  test1.txt

成功例

assert_path_exists "sample/test1.txt"

失敗例

assert_path_exists "sample/test2.txt"
Expected path 'sample/test2.txt' to exist.

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_path_exists

assert_predicate(o1, op, msg = nil)

o1.__send__(op)が真の場合にアサーションに成功します。
テストしたいopが複数ある場合は配列で持たせると綺麗に書けそうです。

成功例

ops = %i(all? any?)
ops.each { |op| assert_predicate [1, 2, 3], op }

失敗例

assert_predicate [1, 2, 3], :empty?
Expected [1, 2, 3] to be empty?.

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_predicate

assert_raises(*exp) { || ... }

*expに指定した例外がどれか一つでもブロック内で発生した場合にアサーションに成功します。
最後に文字列を指定するとメッセージとして扱われ、アサーションの失敗時に出力されます。
また、アサーションに成功した場合は例外クラスを返します。

成功例

assert_raises(ZeroDivisionError) do
  1 / 0
end

# 例外が戻り値となる
exp = assert_raises(ZeroDivisionError, ArgumentError) do
  [1, 2, 3].include?
end
assert_kind_of ArgumentError, exp

# ブロック内で例外が複数起きる場合は先に起きた例外が戻り値となる
exp = assert_raises(ZeroDivisionError, ArgumentError) do
  1 / 0
  [1, 2, 3].include?
end
assert_kind_of ZeroDivisionError, exp

失敗例

assert_raises(ZeroDivisionError) do
  1 / 1
end
ZeroDivisionError expected but nothing was raised.

失敗例(メッセージ有り)

assert_raises(ZeroDivisionError, ArgumentError, "No Error") do
  [1, 2, 3].include?(1)
end
No Error.
[ZeroDivisionError, ArgumentError] expected but nothing was raised.

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_raises

assert_respond_to(obj, meth, msg = nil)

obj.respond_to?(meth)が真の場合にアサーションに成功します。

成功例

assert_respond_to([1, 2, 3], :shift)
assert_respond_to({name: "TomoProg"}, :dig)

失敗例

assert_respond_to([1, 2, 3], :unknown_method)
Expected [1, 2, 3] (Array) to respond to #unknown_method.

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_respond_to

assert_same(exp, act, msg = nil)

obj.equal?(act)が真の場合にアサーションに成功します。
equal?object_idで比較するため、assert_equalとは違う結果となる場合があります。

成功例

assert_same 1, 1
assert_same :sym, :sym
assert_same nil, nil

失敗例

assert_same [1, 2, 3], [1, 2, 3]
Expected [1, 2, 3] (oid=47357985755040) to be the same as [1, 2, 3] (oid=47357985755100).

assert_equalと違う結果になる例

# 中身が同じ配列を準備する
array1 = [1, 2, 3]
array2 = [1, 2, 3]

# assert_equalでは成功する
# ==で比較するため、中身が同じであれば真となる
assert_equal array1, array2

# assert_sameでは失敗する
# equal?で比較するため、object_idが異なると偽となる
assert_same  array1, array2

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_same

assert_send(send_ary, m = nil)

このメソッドは非推奨のようです。
理由を探したところ、下記のサイトに記述がありました。
日々雑記 — Minitest 5.10.0でdeprecateになった機能

assert_sendがdeprecateになった理由がちょっとわからなかったのでtwitterで聞いてみた所、単純に使っている所を見たことが無い為、との事でした。

send_aryに第一要素にレシーバとなる任意のオブジェクト、第二要素にメソッド名、 第三要素にパラメータをそれぞれ指定した配列を指定し、指定したオブジェクトのメソッドが真であればアサーションに成功します。

なぜかこのメソッドだけメッセージの引数名がmsgではなくmでした。

成功例

assert_send [[1, 2, 3], "include?", 1]
DEPRECATED: assert_send.

失敗例

assert_send [[1, 2, 3], :include?, 4]
DEPRECATED: assert_send.
Expected [1, 2, 3].include?(*[4]) to return true.

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_send

assert_silent() { || ... }

標準出力、標準エラー出力に何も出力されない場合にアサーションに成功します。
assert_output("", "")と書くのと同じ意味です。

成功例

assert_silent { 1 + 1 }

失敗例(標準出力)

assert_silent { puts "hello" }
In stdout.
--- expected
+++ actual
@@ -1,3 +1,2 @@
-# encoding: UTF-8
-#    valid: true
-""
+"hello
+"

失敗例(標準エラー出力)

assert_silent { warn "warning" }
In stderr.
--- expected
+++ actual
@@ -1,3 +1,2 @@
-# encoding: UTF-8
-#    valid: true
-""
+"warning
+"

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_silent

assert_throws(sym, msg = nil) { || ... }

指定したブロック内でsymに対してthrowされるとアサーションに成功します。

成功例

assert_throws(:exit) do
  [1, 2, 3].each do |a|
    [4, 5, 6].each do |b|
      throw :exit
    end
  end
end

失敗例

assert_throws(:exit) do
  [1, 2, 3].each do |a|
    [4, 5, 6].each do |b|
      throw :unknown
    end
  end
end
Expected :exit to have been thrown, not :unknown.

ドキュメント
http://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_throws

まとめ

Minitest::Assersionsのassert系のメソッドをすべて試してみました。
Minitestを業務で使い始めてもうすぐ3年が経ちますが、こんなにあったとは知りませんでした。

様々なassertメソッドを試してみて一番感じたのは
適切に使い分けることで失敗の原因がより分かりやすくなる
という点です。

例えば、以下のアサーションは結果はどちらも失敗です。
しかし、後者のほうが何が原因で失敗したのかを分かりやすく伝えてくれます。

assert [1, 2, 3].include?(0) # Expected false to be truthy.
assert_includes [1, 2, 3], 0 # Expected [1, 2, 3] to include 0.

適切なメソッドを使い分け、分かりやすいテストコードを書いていきましょう。

それではまた。

TomoProg

45
36
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
45
36