PHPUnit 8 でassertArraySubsetは非推奨Warning

PHPUnit 7 から PHPUnit 8 へアップデートしたら、次のWarning が表示されました。

assertArraySubset() is deprecated and will be removed in PHPUnit 9.Code language: plaintext (plaintext)

おー、なんてこと!
PHPUnit 9 で assertArraySubset は廃止されてしまうんですね...

修正前

$actual = 大きめの配列

$expected = [
    'Summary' => [
        'error_code'    => '00001',
        'error_message' => 'パラメータに誤りがあります',
        'summary_id'    => 1,
    ],
    'Details' => [
        [
            'error_codes' => '0001 0002',
            'summary_id'  => 1,
            'detail_id'   => 1,
        ],
        [
            'error_codes' => '0003 0004',
            'summary_id'  => 1,
            'detail_id'   => 2,
        ],
    ],
];

$this->assertArraySubset($expected, $actual);Code language: PHP (php)

案1

まず試したのは、配列要素を一つづつ assertEquals で比較する方法です。

$this->assertEquals('0001', $actual['Summary']['error_code']);
$this->assertEquals('パラメータに誤りがあります', $actual['Summary']['error_message']);
$this->assertEquals(1, $actual['Summary']['summary_id']);

$this->assertEquals('0001 0002', $actual['Details'][0]['error_codes']);
$this->assertEquals(1, $actual['Details'][0]['summary_id']);
$this->assertEquals(1, $actual['Details'][0]['detail_id']);

$this->assertEquals('0003 0004', $actual['Details'][1]['error_codes']);
$this->assertEquals(1, $actual['Details'][1]['summary_id']);
$this->assertEquals(2, $actual['Details'][1]['detail_id']);
Code language: PHP (php)

んー、読みにくいですね。assertEquals が大量に並んでいると読む気がしないですね。

$actual がどんな構造なのかピンとこないですし。

例えば、L10 を次のようにタイポしていても気づきにくいです。

$this->assertEquals(1, $actual['Details'][0]['summary_id']);Code language: PHP (php)

これはボツにしたいですね。

案2

assertArraySubset だけでPHPUnitとは別でパッケージ公開されています。

sebastianbergmann(PHPUnitのオーナー)が言っていたように、assertArraySubset のコードを別パッケージにしたぜ

https://github.com/sebastianbergmann/phpunit/issues/3494#issuecomment-464464423
dms/phpunit-arraysubset-asserts - Packagist
This package provides ArraySubset and related asserts once deprecated in PHPUnit 8
GitHub - rdohms/phpunit-arraysubset-asserts: Provides assertArraySubset for use in PHPunit
Provides assertArraySubset for use in PHPunit. Contribute to rdohms/phpunit-arraysubset-asserts development by creating ...

composer でパッケージインストールし、

composer require --dev dms/phpunit-arraysubset-assertsCode language: JavaScript (javascript)

テストに次の2行を挿入します。

use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts; // ★コレ

class ExampleTest extends TestCase
{
    use ArraySubsetAsserts; // ★コレCode language: PHP (php)

すると、$this->assertArraySubset は変更することなく使えます。

これがいいかなと思ったんですが、なぜ assertArraySubset を廃止にすることになったのか気になったので、もう少し調べると...

assertArraySubset はアサーションに失敗したときのメッセージが不親切なんですね。
配列全体を表示するので、どこが違ったのかがわからず、あらためて調べなければならないんです。

この表示を改善してほしいという issue が上がっていたようですが...

sebastianbergmann が出した答えが

下位互換性を維持したまま、この表示を改善することはできないという結論に達した

https://github.com/sebastianbergmann/phpunit/issues/3494#issue-401764930

「assertArraySubset はアサーションに失敗したときのメッセージが不親切」たしかにこれは問題ですね

案3

配列の要素を一つづつ assertEquals で検証するのではなく、$expected の構造のデータを作って、$expected と assertEquals するようにしました。

$actual = 大きめの配列

$expected = [
    'Summary' => [
        'error_code'    => '00001',
        'error_message' => 'パラメータに誤りがあります',
        'summary_id'    => 1,
    ],
    'Details' => [
        [
            'error_codes' => '0001 0002',
            'summary_id'  => 1,
            'detail_id'   => 1,
        ],
        [
            'error_codes' => '0003 0004',
            'summary_id'  => 1,
            'detail_id'   => 2,
        ],
    ],
];

$actualSubset = [
    'Summary' => [
        'error_code'    => $actual['Summary']['error_code'],
        'error_message' => $actual['Summary']['error_message'],
        'summary_id'    => $actual['Summary']['summary_id'],
    ],
    'Details' => [
        [
            'error_codes' => $actual['Details'][0]['error_codes'],
            'sumamry_id'  => $actual['Details'][0]['summary_id'],
            'detail_id'   => $actual['Details'][0]['detail_id'],
        ],
        [
            'error_codes' => $actual['Details'][1]['error_codes'],
            'sumamry_id'  => $actual['Details'][1]['summary_id'],
            'detail_id'   => $actual['Details'][1]['detail_id'],
        ],
    ],
];

$this->assertEquals($expected, $actualSubset);Code language: PHP (php)

失敗したときのメッセージ(推測)

-- Expected
+++ Actual
@@ @@
 Array (
     'Summary' => Array (
-        'error_code' => '00001'
+        'error_code' => '00002'
         'error_message' => '英数字以外を含みます'
         'id' => 1
     )
     'Details' => Array (...)
 )Code language: plaintext (plaintext)

テストコードは長くなりましたが、案1 より読みやすくなりました。

そして、失敗メッセージを見るだけで、どの要素で失敗したのかがわかるようになりました。

タイトルとURLをコピーしました