読者です 読者をやめる 読者になる 読者になる

そろそろPower Assertについてひとこと言っておくか

Test

タイトルはもちろん釣りで・・・はない!

ちょっと真面目に、Power Assertについて意見を述べたいのです。

そもそもPower Assertって何?

てきとーに説明すると、

普通の比較演算子で普通にassert書けば、失敗時に各部分式の値を表示してくれる

ようなものです。 Groovy製のテスティングフレームワークであるSpockがおそらく本家大本です((要出典。こういう系の発想は割と昔からあったし、Spock以前に実装例がありそうな気がする。そもそも、Spockは最初からPower Assert持ってたのかも調べないといけない。ちなみに、式木を弄ってAssertを組み立てる、というものであれば(PowerAssertよりも情報量は少なくなるものだけど)、自分の知る限りだと2009年6月にこんな記事があります。 http://themechanicalbride.blogspot.jp/2009/06/better-unit-tests-with-testassert-for.html まずはこの時点でのSpockの実装を確認せねば・・・))。

Groovyでこう書くと、

def xs = [0,1,2,3,4]
assert 1 == xs.min() 

こうなります。

Exception thrown
10 02, 2013 2:57:46 午後 org.codehaus.groovy.runtime.StackTraceUtils sanitize

WARNING: Sanitizing stacktrace:

Assertion failed: 

assert 1 == xs.min()
         |  |  |
         |  |  0
         |  [0, 1, 2, 3, 4]
         false


    at org.codehaus.groovy.runtime.InvokerHelper.assertFailed(InvokerHelper.java:399)
以下略

おお!値がどうなったか一目瞭然ですね!

Power Assertをユニットテストに使う

どこがどうなったってアサーションに失敗したのかが分かりやすいため、 これをユニットテストアサーションとして採用する流れがあります。

こんな感じですね。

import groovy.transform.Canonical

@Canonical
class User {
  def name
  def age
}

def a = new User("hoge", 10)
assert a == new User("hoge", 20)
Exception thrown
10 02, 2013 3:05:37 午後 org.codehaus.groovy.runtime.StackTraceUtils sanitize

WARNING: Sanitizing stacktrace:

Assertion failed: 

assert a == new User("hoge", 20)
       | |  |
       | |  User(hoge, 20)
       | false
       User(hoge, 10)
略

Groovy知らなくても、何が起こっているのかはよくわかると思います。

何が起こっているかは、確かに一目瞭然なのですが・・・

俺たちが欲しかった情報はなんだ?

ユニットテストにおいて、最も欲しいのは「どこがどうなっているか」ではなく、 「どこがどう違っているか」じゃないですかね。

「どこがどうなっているか」だけ渡されても、「どこがどう違っているか」は目視で確認しなけりゃならんのです。 だるいのです。 先の例くらいならまだマシですけど、長い文字列とかだと探すの大変です。

import groovy.transform.Canonical

@Canonical
class SomeData {
  String str
  int i
}
Assertion failed: 

assert a == new SomeData("very long long long string", 20)
       | |  |
       | |  SomeData(very long long long string, 20)
       | false
       SomeData(very long long long sting, 19)

19に釣られて、very long long long stringとvery long long long stingの違いを見抜けなくて(本来)無駄なRedになってしまっても、それは仕方がないことですよね。

本当に欲しい情報って、例えばこんなものじゃないですかね?

Assertion failed:

equality check is failed.
difference:
 - SomeData.str: ["...st(-)ing", "...st(r)ing"]
 - SomeData.i: ["(19)", "(20)"]

この下に、どこがどうなったか情報があったら重宝はすると思います。 が、それが最初じゃないでしょう、と言いたいのです。

じゃぁお前が実装しろよ

ここで、「なので実装しました!」とか言えたら超かっちょいいんですけど、 (社内用テスティングフレームワークとして)作りかけて止まっちゃってます・・・
ちょっと別の色々(LangExtとか)に時間が取られちゃってまして・・・

でも、自分が欲しいのは正直こういう形の情報なんですよね。 PowerAssert的な情報は、あると便利だけどそれだけあっても辛いのです。

なので、このエントリの意見に同意してくれて、時間ある人は是非作ってみてほしいんですよね。 Power Assertに「欲しかったのはお前じゃないんだ!」を突き付けたい!!!