GameWith Developer Blog

GameWith のエンジニア、デザイナーが技術について日々発信していきます。

GameWithのPHPUnitを4から8にバージョンアップした話 #GameWith #TechWith

こんにちは!GameWithのサーバサイドエンジニアのkuromokaです!

先日、GameWithのサイトをPHP7.3にバージョンアップした記事を書きました。

tech.gamewith.co.jp

ただPHPUnitのバージョンはそのまま4系を使い続けていて、4系だとPHP7に対応していないために次のような問題点がありました。

  • 新規にPHP7.3のコードは書けるけどテストコードが書けない
  • 既存のテストコードが書かれている箇所を、PHP7.3のコードに変更するとテストコードが失敗

そのため今回、現行の最新バージョンであるPHPUnit 8へのバージョンアップを行いました。

対応内容

そのままパッケージのバージョンを上げただけでは当然動かなかったので、次の対応を行いました。

DbUnitの変更

DB関連のテストコードでは、DbUnitというパッケージを使っています。今回の対応ではこれも合わせてバージョンアップをする必要がありました。

ただし本家のsebastianbergmann/dbunitは、更新がストップしているためにPHPUnit 8に対応していませんでした。そのためDbUnitの依存を外すことも考えたのですが、コード中で使われている箇所が多く断念しました。

どうしようか?といろいろと検索しているうちにforkされたパッケージがいくつかあるのを知り、一番スター数の多かったkornrunner/dbunitを使うことにしました。現行の最新バージョンの4.1.0なら、PHPUnit 8にも対応しています。

packagist.org

テストクラスの変更

次のクラスがPHPUnitとDbUnitで名前空間ベースに変更されていて、クラスが見つからないとエラーが出るため修正しました。

  • PHPUnit

    • PHPUnit_Framework_TestCase
      PHPUnit\Framework\TestCase
  • DbUnit

    • PHPUnit_Extensions_Database_TestCase
      PHPUnit\DbUnit\TestCase
    • PHPUnit_Extensions_Database_DataSet_YamlDataSet
      PHPUnit\DbUnit\DataSet\YamlDataSet

前後処理のメソッドへのvoidの追加

テストメソッド実行前の処理を書くsetUP()など、PHPUnit\Framework\TestCaseでvoidが追加になったメソッドがあります。*1

そのため継承したテストケースのクラスでも同じように、voidを追加する必要がありました。

<?php

class AppTest extends \PHPUnit\Framework\TestCase
{
-   protected function setUp()
+   protected function setUp(): void
    {
        ・・・
    }
}

GameWithで書かれているテストコードでは次のメソッドが対象でした。

  • setUP()
    setUp(): void
  • tearDown()
    tearDown(): void

例外テストの変更

setExpectedException()が廃止になっているため、expectException()に変更をしました。

例外アノテーションの変更

@expectedException のアノテーションでの例外テストはPHPUnit 9で消える予定のため、実行すると次のようなwarningメッセージが出てきます。そのため同様にexpectException() への変更を行いました。

The @expectedException, @expectedExceptionCode, @expectedExceptionMessage, and @expectedExceptionMessageRegExp annotations are deprecated. They will be removed in PHPUnit 9. Refactor your test to use expectException(), expectExceptionCode(), expectExceptionMessage(), or expectExceptionMessageRegExp() instead.

無意味なテストの修正

「無意味なテスト」とは、ドキュメントの「リスクを伴うテスト」の章に書いてあるテストのことです。GameWithのテストコードではこの「無意味なテスト」が非常に多く今回の修正で一番大変な部分でした。オプションでチェック無効にもできるみたいですが、結局はコード負債として残ってしまうため一緒に直すことにしました。

phpunit.readthedocs.io

いろいろな「無意味なテスト」のパターンはあったのですが、たとえばreturnしない関数へのテストで、アサーションが何もないようなテストコードがありました。
実際のコードでは呼び出し後にモックの引数チェックなどが続いてたりしたのですが、肝心のアサーションがないのでThis test did not perform any assertionsというメッセージが出ていました。

<?php

function void_func(): void
{
    ・・・
}
<?php

class AppTest extends \PHPUnit\Framework\TestCase
{
    public function test_void_func()
    {
        void_func();
    }
}

このパターンのテストコードは、assertNull()でNULLかどうかのアサーションを追加して対処しました。

<?php

class AppTest extends \PHPUnit\Framework\TestCase
{
    public function test_void_func()
    {
        $result = void_func();
        $this->assertNull($result)
    }
}

他の「無意味なテスト」の修正についても書くと、何日掛かっても書き終わらないので今回はこれだけにしておきます!

修正差分

今回のバージョンアップ対応を含めたPRは、Composerの定義ファイルの更新なども含まれていますが、

  • Files changed: 105
  • +2,471 −5,554

となかなかの大工事になりました。

f:id:kuromoka16:20191127203933p:plain

まとめ

これまで書いてきた対応を行ったことで、最初に書いた問題点を解決することができました!

  • 新規にPHP7.3のコードは書けるけどテストコードが書けない
  • 既存のテストコードが書かれている箇所を、PHP7.3のコードに変更するとテストコードが失敗

と言いたかったのですが、GameWithではモックのフレームワークにAspectMockを使っていて、こちらもPHP7.3に対応したバージョンに上げる対応を別途行いました(泣)
現在は対応が完了して、上記の問題点は解決しています!

終わりに

GameWithのDeveloper向けTwitterアカウントを開設しました。

技術やブログの更新情報などを発信するので良かったらフォロー宜しくお願いします!

twitter.com

*1:網羅的に書いてあるページは見つけられなかったのですが、https://phpunit.readthedocs.io/en/latest/fixtures.html などを参考にしました。