PHPUnitのassertArraySubset()について

  • 9
    いいね
  • 3
    コメント

すげー便利そうに見えて、微妙に使いづらい子だな…という印象。(言いたいこと終わり)
https://phpunit.de/manual/current/ja/appendixes.assertions.html

配列のアサーションの問題

例えば、ある関数の実行結果がこういう感じの配列をだとしよう。

[
  [
    'name' => 'taro',
    'gender' => 'male',
    'updated' => '2015-08-10 12:01:13',
  ],
  [
    'name' => 'hanako',
    'gender' => 'female',
    'updated' => '2016-07-31 13:04:14',
  ],
];

関数のテストを書くなら、↑の配列をexpectとして用意して、結果をassertEqualsで比較すればいい。

ところが、別に全部の要素を厳密に比較しなくてもいいことがある。例えば、配列内のupdatedという名前のフィールドはデータが更新されたタイミングで自動的に採番されるというロジックなら、厳密な時間をテストしようとすると大変だし、やる必要もないだろう。丁寧にやるとしてフォーマットのチェックぐらいだろうか。

「updatedは無視して、それ以外だけをアサーションしたい」

こういう時、アサーションがassertEquals一発で書けなくなってしまう。
仕方がないので、べたっと書いてみるけれど、

$this->assertEquals('taro', $result[0]['name']);
$this->assertEquals('male', $result[0]['gender']);
$this->assertEquals('hanako', $result[1]['name']);
$this->assertEquals('female', $result[1]['gender']);
//...

ウッ…。格好良くないし、かなり記述量が増えてしまう。
だいたい、関数の実行結果が配列形式でなければ、$result[0]の辺りでエラーになってしまうだろう。失敗した時のログが読みにくくなってしまってよくない。

assertArraySubset

たぶんこのアサーションは、この問題を解決する目的なのではなかろうか。
配列に対するassertEqualsに似ているがちょっと挙動が異なる。

部分配列 をexpectに指定できるのである。

$expect = [
    [
        'name' => 'taro',
        'gender' => 'male',
    ],
    [
        'name' => 'hanako',
        'gender' => 'female',
    ],
];
$actual = [
    [
        'name' => 'taro',
        'gender' => 'male',
        'updated' => '2015-08-10 12:01:13',
    ],
    [
        'name' => 'hanako',
        'gender' => 'female',
        'updated' => '2016-07-31 13:04:14',
    ],
];

$this->assertArraySubset($expect, $actual);

部分配列とは、検証対象の配列から幾つか要素を省くことで作られる配列である。この場合、updatedという要素を省いた配列で検証しているけど、ちゃんとassertが通る。

アサーション一個で書けて簡潔だし、そんなに読みにくくもないと思う。いいじゃんこれ。…と思ったんですよ。

微妙に使いづらい点

assertEqualsは失敗した際のログがすごく分かりやすい。配列同士の場合、違いをdiff形式で表示してくれる。

Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
 Array (
     0 => Array (
         'name' => 'taro'
         'gender' => 'male'
+        'updated' => '2015-08-10 12:01:13'
     )
     1 => Array (
         'name' => 'hanako'
         'gender' => 'female'
+        'updated' => '2016-07-31 13:04:14'
     )
-    2 => Array (...)
-    3 => Array (...)
-    4 => Array (...)
-    5 => Array (...)
-    6 => Array (...)
-    7 => Array (...)
-    8 => Array (...)
-    9 => Array (...)
-    10 => Array (...)
 )

一方でassertArraySubsetはexpectをダンプするだけのようだ。。これではどこがダメだったのかよくわからず、失敗した時に役に立ちづらいテストになってしまう。

Failed asserting that an array has the subset Array &0 (
    0 => Array &1 (
        'name' => 'taro'
        'gender' => 'male'
    )
    1 => Array &2 (
        'name' => 'hanako'
        'gender' => 'female'
    )
    2 => Array &3 (
        'name' => 'hanako'
        'gender' => 'female'
    )
    3 => Array &4 (
        'name' => 'hanako'
        'gender' => 'female'
    )
    4 => Array &5 (
        'name' => 'hanako'
        'gender' => 'female'
    )
).

俺が求めているのはassertEqualsと同じようなdiff表示なのだが、これ何とかならないかしら…

結局、余分なものを排除してからassertEqualsで比較する手も考えたほうがいいのかなあ。。