ユニットテスト(PHPの場合は、phpunit)を使い始めて、2,3年。その重要さは理解しているものの、つい最近まで、コードの大変さによりなかなか多用はしていませんでした。

データベース絡みや入力画面絡みのテスト、書くのはやっかいです。しかし、ウェブのアプリの開発には、データベースや入力画面があって当然。ここのテストを自動化をせずにいったいどこでする?とも思う。

しかし、どうしてやっかいなのでしょう、これら?

例えば、前回の会員登録の件では、テストとして考えられるのは、

まずは、エラーを出して確認するテストたち:

  • 必須の項目に値がないときにエラーとなるか。
  • EメールにEメールでないものを入力したときにエラーとなるか。
  • すでに登録した会員のEメールを入れたときにエラーとなるか(重複チェック)。

そして、エラーがなく、会員登録成功したときの確認のテストも必要です。

前者のエラーを出して確認するテストは、コントローラーでなくモデルに入れてテストが可能かもしれません。ララベルのバリデーションはすでに一貫しているし、カスタマイズのバリデーションも同様にテスト可能です。

しかし、それらユニットテストは個々のバリデーションをテストするだけで、それら集合した入力画面に対してのファンクショナルテストやアクセプタンステストは難しいです。あたかもユーザーが入力するようなテストです。

さらに、後者のエラーが出ない成功の確認のテストは、実際にDBにレコードが作成されてしまいます。つまり1回成功テストすると、次回は重複でエラーとなり成功のテストができなくなります。もちろん、毎回レコードを削除するという手段もありますが(これはDBテストとして将来に紹介します)、違うDBを用意するとかお膳立てがかなり面倒です。

もちろん、ここで、ララベルがそのフレームワークを活かしたユニットテストの登場です(注意:ララベル5.1での前提です)。

まずは、Eメールアドレスの重複による失敗のテストです。

class SignupTest extends TestCase
{
    public function testSignupFail()
    {
        $this->visit('/signup')
            ->type('dup@gmail.com', 'email')
            ->type('testtest', 'password')
            ->type('testtest', 'password_confirmation')
            ->type('山田', 'last_name')
            ->type('太郎', 'first_name')
            ->press('保存')
            ->dontSee('会員登録完了');
    }
}

このテストは、ユーザーが以下のような画面を経験したと同じ状況をテストします。すでにDBにdup@gmail.comが存在していると仮定です。

fail

16行目の “dontSee(‘会員登録完了’);” では、エラーになるために成功時の「会員登録完了」の文字が画面には見えないよ、つまりdon’t seeということです。

今度は、成功時のテストですが、

use IlluminateFoundationTestingWithoutMiddleware;
use IlluminateFoundationTestingDatabaseMigrations;
use IlluminateFoundationTestingDatabaseTransactions;

class SignupTest extends TestCase
{
    use DatabaseTransactions;

    public function testSignupSuccess()
    {
        $this->visit('/signup')
            ->type('success@gmail.com', 'email')
            ->type('testtest', 'password')
            ->type('testtest', 'password_confirmation')
            ->type('山田', 'last_name')
            ->type('太郎', 'first_name')
            ->press('保存')
            ->see('会員登録完了');
    }
}

先の失敗の例と違って、今度はdon’t seeでなくsee(‘会員登録完了’)となっています。

しかし会員登録完了なのにDBのレコードは作成されません。そう、7行目のuse DatabaseTransactionsの宣言により、レコードの作成を試みるもののDBのトランザクション機能を使用してわざと変更をロールバックさせています。これにより作成あるいは変更されたデータを手動で戻さずに、何回でもテストの実行が可能となるわけです。もちろん、トランザクション機能があるDB、mysqlならinnodbの使用でないとできないことです。

先の2つテストを合わせて、実行すると、「OK」という結果です。

$ phpunit tests/SignupTest
PHPUnit 4.8.2 by Sebastian Bergmann and contributors.

..

Time: 313 ms, Memory: 27.25Mb

OK (2 tests, 7 assertions)

By khino