simpletestからPHPUnitに移行したときの話

JUGEMテーマ:PHP


つい先日、テスティングフレームワークをsimpletestからPHPUnitに移行しました。
その時にやったこと・困ったことの知見を共有します。

2016年の今に需要のある情報だとは思えませんが、今でもsimpletestでテストを書いていて、そこから脱出したい方のお役に立てれば幸いです。

simpletest_to_PHPUnit.png

移行前の状態

14万行くらいのリポジトリの中にsimpletestによるテストコードは1000行くらいある状態で、
カバレッジ計測はしてませんでしたが、それほどテストコードがある状態ではありませんでした。
そのため移行はスムーズに出来ましたが、仮にコード量が多くてもそこまで苦労はしないんじゃないのかなと思います。

何故PHPUnitを選択したのか?

PHPUnitがPHP界隈のテスティングフレームワークでほぼデファクトスタンダードなので安心感がありました。
しかし、なによりも simpletestとほぼメソッドが一緒で移行が楽 なのが最大の理由です。
数時間の作業でさくっと移行出来ちゃいました。

やったこと

phpunit.xmlを作る

composerからPHPUnitを入手してから、まず最初にphpunit.xmlを作りました。
phpunit.xmlとはPHPUnitのconfigurationを指定するファイルで、テスト実行時のカレントディレクトリに置いてあれば自動で読み込んでくれます。
とりあえず最低限必要な項目は以下くらいだと思います。


<?xml version="1.0" encoding="UTF-8"?>
<phpunit
    bootstrap="vendor/autoload.php"
    colors="true"
    convertErrorToExceptions="false"
    convertNoticesToExceptions="false"
    convertWarningToExceptions="false">
</phpunit>

  • bootstrap

    • 全テストで最初にcomposerのautoloaderを読ませる指定です。

    • PHPUnitをcomposerから読み込めなければならないので必須。

  • colors

    • phpunitの結果出力に色がついて見やすくなります。必須。

  • convertXXXXXToExceptions

    • テスト中に発生したError/Notices/WarningをExceptionに変換して、テストをコケさせるための設定を、わざとfalseにして無効化しています。

    • たぶんtrueにするのがあるべき姿ですが、レガシーだとなにかとNoticesやWarningが発生するもの・・・。それらを全部解消しないとテストが通らないのはつらすぎるのでfalseにしています。

既存のテストをPHPUnitで動かす

次に既存のテストケースをPHPUnitで動かすようにしました。
やることは簡単で、テストクラスの継承元を  UnitTestCase  から  PHPUnit_Framework_TestCase  に変えて、テストクラス名と ファイル名(忘れがち) の接尾に  Test  をくっつけるだけ。
ホントこれだけ。


- class MyClass extends UnitTestCase
+ class MyClassTest extends PHPUnit_Framework_TestCase

基本的にテストクラス内部のテストの書き方はsimpletestもPHPUnitもほぼ一緒です。以下のルールはどちらにも当てはまります。なので上の修正だけでだいたい動いちゃいます。

  • テストメソッドの接頭に  test  をつける(例: testSameCheckMethod

  • テストケース毎の事前・事後処理を  setUp  と  tearDown  に書く

  • テストケース毎に  $this->assertTrue  などでアサーションを行う

互換性の無いアサーションメソッドを修正する

アサーションメソッドもsimpletestとPHPUnitはほぼ互換性はあるのですが、一部ちょっとだけ違うものがあったりするので修正します。
うちで変換の必要があったのは以下の2つでした。

  • assertTrue  の判定が厳密になった

    • simpletest: いわゆる  ==  と同じ評価で、true, 1, '1'などphp的にtrueなのは全部通る

    • PHPUnit: いわゆる  ===  と同じ評価で、boolean型の  true  のみを通す

  • assertEqual  や  assertPattern  のメソッド名が変わった

    • assertEqual  ->  assertEquals

    • assertPattern  ->  assertRegExp

    • 名前が変わっただけで引数などは一緒です。grep一発置換しましょう。

困ったこと

テストオブジェクトの準備方法が違う!

1つだけ困ったことがありまして、テストクラスからテストオブジェクトを作り出しテストを実行するという処理の仕方が、simpletestとPHPUnitで若干違っていました。
まあうちが グローバルオブジェクトに依存したテストをしてしまっている(!?) のが最大の原因なので、世の真面目なテストを作っている方々には関係のない話だと思います!!

  • simpletest: テストケース実行直前にテストオブジェクトを作る

  • PHPUnit: 事前にテストオブジェクトを全て作ってから、順番にテストケースを実行する

ざっと書くとこんな違いで、ちゃんとしたテストを作っているならたぶん困らないのですが、 コンストラクタでグローバルオブジェクトを作って、そのグローバルオブジェクトを使ったテストを行って いたりすると困ります。
図で書くと以下のような感じです。

Untitled (8).png

こんな感じでPHPUnitは最初にすべてのテストオブジェクトのコンストラクタが実行されるので、そこで設定したグローバルオブジェクトがテストケース実行時点では変わっていたりします。そのせいで謎のテスト失敗が発生してしまい、調査に時間がかかりました・・・。
対応方法はグローバルオブジェクトの初期化を  setUp  メソッドに移動して、テストケース実行直前に行わせることで解消しました。

アノテーションに  backupGlobals backupStaticAttributes runInSeparateProcess などを設定して解消するやり方もあったのですが、この記事では触れません。

最後に

うちの移行はかなりスムーズにいった方だと思います。
ここには僕の移行過程で分かった範囲で書いてますが、他にも地味に違うところはあるかもしれません。
うちでは使ってませんでしたが、simpletestのモック機能はだいぶ違うみたいです。
simpletestのエッジな機能を使っていると、他にも困ったことが出てくるかもしれませんが、メインとなる機能(テストクラス・テストメソッド・アサーション)の範囲ではびっくりするくらい互換性があります。

モックライブラリやテストデータファクトリなど、世の中の便利なテストライブラリはだいたいPHPUnitと親和性が高く作られています。
世の強力なツールによるサポートを受け、開発効率を上げるためにもsimpletestからPHPUnitに移行していきましょう!

スポンサーサイト

コメント