4
Help us understand the problem. What are the problem?

posted at

updated at

assertEqualsやassert_equalの引数はなぜ expected, actual の順なのか、調べてみた

はじめに

xUnit系のテスティングフレームワークでは、2つの値が等しいことを検証するメソッド(assertEqualsassert_equalなど)の引数が expected, actual(期待する値、実際の値)の順に並びます。

// Java (JUnit)
assertEquals(2, calculator.add(1, 1));
# Ruby (minitest または test-unit)
assert_equal(2, calculator.add(1, 1))

しかし、この引数の順番は直感に反するので、逆に書きたい、と思う人も中にはおられるようです(というか、僕も昔そう思っていました)。

// actual、expectedの順がいい!
assertEquals(calculator.add(1, 1), 2);
# actual、expectedの順がいい!
assert_equal(calculator.add(1, 1), 2)

そこで、expected, actualの順で引数が並ぶのには何か理由があるのか、ちょっと調べてみました。

調査方法

"unit test assert equal argument order why" みたいなキーワードでググってみました。検索の上位に毎度お馴染みのStack Overflowのリンクが上がってきたので、いくつか回答を覗いてみました。

Screen Shot 2022-01-14 at 7.52.24.png

Kent Beck氏は何と言っているか?

JavaのJUnitやRubyのtest-unit/minitestなど、いわゆるxUnit系のテスティングフレームワークはKent Beck氏が始祖の一人であると言われています。

1997年に、Smalltalk のためのユニットテストのフレームワークであるSUnitをもとにして、エーリヒ・ガンマと、SUnitの開発者のケント・ベックが中心となって開発された。

JUnit - Wikipedia

そして、こちらのStack Overflowの中に、この疑問に対するKent Beck氏の回答(のリンク)が載っていました。その回答がこちらです。

Line a bunch of assertEquals in a row. Having expected first makes them
read better. And yes, it's too late to change it.

(筆者訳)
大量の assertEquals をひとまとめにして並べてみる。expectedを最初に持ってきた方が読みやすい。(1つ前のスレッドの意見に対して)そしておっしゃるとおり、今から変更するのはもう手遅れです。

JUnit / RE: [Junit-devel] why not assertEquals(actual, expected)?

コメントが短く、サンプルコードもないのでちょっと意図がつかみにくいのですが、Stack Overflowの回答を見る限り、こういうことのようです。

actual, expected の順だと読みにくい(?)

def test_bit_length
  assert_equal((-2**12-1).bit_length, 13)
  assert_equal((-2**12).bit_length, 12)
  assert_equal((-2**12+1).bit_length, 12)
  assert_equal(-0x101.bit_length, 9)
  assert_equal(-0x100.bit_length, 8)
  assert_equal(-0xff.bit_length, 8)
  assert_equal(-2.bit_length, 1)
  assert_equal(-1.bit_length, 0)
  assert_equal(0.bit_length, 0)
  assert_equal(1.bit_length, 1)
  assert_equal(0xff.bit_length, 8)
  assert_equal(0x100.bit_length, 9)
  assert_equal(0x101.bit_length, 9)
  assert_equal((2**12-1).bit_length, 12)
  assert_equal((2**12).bit_length, 13)
  assert_equal((2**12+1).bit_length, 13)

  assert_equal((-2**10000-1).bit_length, 10001)
  assert_equal((-2**10000).bit_length, 10000)
  assert_equal((-2**10000+1).bit_length, 10000)
  assert_equal((2**10000-1).bit_length, 10000)
  assert_equal((2**10000).bit_length, 10001)
  assert_equal((2**10000+1).bit_length, 10001)

  2.upto(1000) {|i|
    n = 2**i
    assert_equal((-n-1).bit_length, "(#{-n-1}).bit_length", i+1)
    assert_equal((-n).bit_length, "(#{-n}).bit_length",     i)
    assert_equal((-n+1).bit_length, "(#{-n+1}).bit_length", i)
    assert_equal((n-1).bit_length, "#{n-1}.bit_length",     i)
    assert_equal((n).bit_length, "#{n}.bit_length",         i+1)
    assert_equal((n+1).bit_length, "#{n+1}.bit_length",     i+1)
  }
end

expected, actual の順だと読みやすい(?)

def test_bit_length
  assert_equal(13, (-2**12-1).bit_length)
  assert_equal(12, (-2**12).bit_length)
  assert_equal(12, (-2**12+1).bit_length)
  assert_equal(9, -0x101.bit_length)
  assert_equal(8, -0x100.bit_length)
  assert_equal(8, -0xff.bit_length)
  assert_equal(1, -2.bit_length)
  assert_equal(0, -1.bit_length)
  assert_equal(0, 0.bit_length)
  assert_equal(1, 1.bit_length)
  assert_equal(8, 0xff.bit_length)
  assert_equal(9, 0x100.bit_length)
  assert_equal(9, 0x101.bit_length)
  assert_equal(12, (2**12-1).bit_length)
  assert_equal(13, (2**12).bit_length)
  assert_equal(13, (2**12+1).bit_length)

  assert_equal(10001, (-2**10000-1).bit_length)
  assert_equal(10000, (-2**10000).bit_length)
  assert_equal(10000, (-2**10000+1).bit_length)
  assert_equal(10000, (2**10000-1).bit_length)
  assert_equal(10001, (2**10000).bit_length)
  assert_equal(10001, (2**10000+1).bit_length)

  2.upto(1000) {|i|
    n = 2**i
    assert_equal(i+1, (-n-1).bit_length, "(#{-n-1}).bit_length")
    assert_equal(i,   (-n).bit_length, "(#{-n}).bit_length")
    assert_equal(i,   (-n+1).bit_length, "(#{-n+1}).bit_length")
    assert_equal(i,   (n-1).bit_length, "#{n-1}.bit_length")
    assert_equal(i+1, (n).bit_length, "#{n}.bit_length")
    assert_equal(i+1, (n+1).bit_length, "#{n+1}.bit_length")
  }
end

サンプルコードの引用元: https://github.com/ruby/ruby/blob/master/test/ruby/test_integer.rb

引数の位置が安定しやすいから、この順番?

上のサンプルコードを見ただけではまだピンと来ないかもしれません。

StackOverflowの回答にも書いてあるんですが、expectedは文字列や数値といった単純なリテラルを書くことが多く、actualは複雑なメソッド呼び出しや、さまざまな引数を与えることが多いです(もちろん状況によりますが)。

結果、「expectedは短くて長さも安定しやすい」「actualは長くて長さも変わりやすい」ということになります。

そのため、assertEqualsassert_equalを大量に並べる場合は、expectedを最初に持ってきた方が、引数の位置を安定させやすい(そしてexpectedをさっと把握しやすい)、ということのようです。

Screen Shot 2022-01-14 at 8.17.52.png

Screen Shot 2022-01-14 at 8.17.57.png

もちろん、例外パターンもあると思うので、「見ろ、こういう場合はactualが最初に来た方が読みやすいぞ!」という反論はいくらでもできそうですが、Kent Beck氏に言わせるとexpectedが最初に来た方が読みやすくなることが多いのかもしれません。

他のツールはおそらくJUnitを踏襲しただけ

JUnitはもともとSmalltalk用のSUnitをJavaに移植したものですが、assertEqualsが導入されたのはJUnitが最初のようです(参考)。それゆえ、assertEqualsの元祖はJUnitになると思われます。

Rubyのtest-unit/minitestやPythonのunittestライブラリがexpected, actualの順になっているのは、おそらくJUnitの引数の順番を踏襲しただけだと思います。

まとめ

というわけで、この記事ではassertEqualsassert_equalの引数はなぜ expected, actual の順なのか調査した結果を書いてみました。

StackOverflowの回答を読んでいると、回答者自身の「俺はこう思う」的な意見は多いのですが、原典にさかのぼって調査するような回答はほとんど見つけられませんでした。その中で唯一見つけられたのが、前述のKent Beck氏の回答です。

とはいえ、その回答も短く、僕個人も「わかるような、わからんような」という感が否めません。なんとなくの予想ですが、Kent Beck氏自身も「まあ、どっちでもいいんだけどね」ぐらいに考えているような気がします(確固たる理由があるなら、もっと詳しいコメントが見つかりそうなので)。

この件についてもし詳しい理由をご存じの方がいたら、コメント欄等で情報源を教えてもらえると幸いです :pray:

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
4
Help us understand the problem. What are the problem?