BiTT開発
BiTT開発
2019.02.19
#48
バックエンドへの道

『Laravel』でファイルアップロードAPIのテストを書く

エリカ

こんにちは、ディレクターのエリカです。

エンジニアに仕様を伝えるのって大変ですよね。そもそも、仕様を詰めきれてなかったり、抜けてたりすることもありますし。

そこで、テストコードを渡すことができたら、素敵ですよね。Laravelならできちゃいます。

テスト環境の準備

今回もLaradockの利用が前提です。

冪等性を担保するためにテスト実行時は毎回データベースをリセットしたいですから、そのための設定を行います。といっても環境ファイルを変更するだけになります。こうしておけばまかり間違って本番環境でテストを実行してしまっても、データが削除されることはありません。

Laravelをセットアップしたディレクトリの /phpunit.xml には以下のように記述されており、テスト実行時は、 APP_ENVtesting に切り替わります。そのため、 .env.testing という環境ファイルを用意しておけば、自動でその内容が適用されます。

<php>
        <env name="APP_ENV" value="testing"/>
        <env name="BCRYPT_ROUNDS" value="4"/>
        <env name="CACHE_DRIVER" value="array"/>
        <env name="MAIL_DRIVER" value="array"/>
        <env name="QUEUE_CONNECTION" value="sync"/>
        <env name="SESSION_DRIVER" value="array"/>
    </php>

.env.testing では、データベースの設定をテストを実行する環境に合わせて変更しておきます。

DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=testing
DB_USERNAME=test_user
DB_PASSWORD=test_password

Laradockを利用している場合はこんな感じです。また、 test_user が操作可能な testing データベースを作成しておきます。

テストコードの準備

artisan コマンドを使います。

Laradock を利用している場合は、 workspace コンテナに接続します。

‌artisan make:test ApiTest を実行すると、 tests/Feature/ApiTest.php が作成されます。

namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;

class ApiTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testExample()
    {
        $this->assertTrue(true);
    }
}

テストを実行してみます。

phpunit ./tests/Feature/ApiTest

PHPUnit 7.4.5 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 3.11 seconds, Memory: 18.00MB

OK (1 test, 1 assertion)

初期で設定されているテストのみですので、成功します。

テストコードの作成

それでは、テストを書いていきます。

テストのたびにデータベースのマイグレーションリセットが自動で実行されるように、トレイトを指定します。

namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;

class ApiTest extends TestCase
{
        use RefreshDatabase; // 追加 
        
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testExample()
    {
        $this->assertTrue(true);
    }
}

こうしておくことにより、テストのたびに artisan migration:reset 相当の処理が行われます。

正常系のテスト

ファイルのアップロードを受けつけるAPIがあると想定し、そのAPIを利用したファイルのアップロードが成功するか、というテストコードを書いてみたいと思います。

public function testFileUpload()
    {
            // 1
        \Storage::fake('files');

                // 2
        $file = UploadedFile::fake()->image('dummy.jpg', 800, 800);

                // 3
        $this->json('POST', '/api/v1/files', [
            'file' => $file,
        ]);

                // 4
        \Storage::disk('files')->assertExists($file->name);
    }
  1. \Storage::fake('testing'); テスト用のモックストレージを用意します。
  2. ダミーの画像ファイルを用意します。
  3. ファイルアップロードAPIにデータを送信します。
  4. ストレージのモックに送信したファイルが保存されているかをテストします。

これは、想定どおりの動作を確認するための正常系のテストになります。

次に異常系のテストコードを書いてみたいと思います。

異常系のテスト

次に、対象外のファイルが送信されたときにファイルが保存されないことをテストするコードです。

public function testFailFileUpload()
    {
        \Storage::fake('testing');

                // 1
        $file = UploadedFile::fake()->image('dummy.jpg', 80, 80);

        $this->json('POST', '/api/v1/files', [
            'file' => $file,
        ]);

                // 2
        \Storage::disk('testing')->assertMissing($file->name);
    }
  1. ダミーの小さい画像を作成します。
  2. モックストレージに保存されていないことをテストしています。

これは、画像の縦横のサイズに制限がある場合に、その制限に引っかかったため、アップロードに失敗するということを意図したテストになります。

まとめ

正常系のテストコードは、APIを実装していない場合はもちろん失敗しますが、このようにさまざまなテストをコードベースで準備することができます。

仕様を決めてテストコードを先に記述しておけば、エンジニアが仕様に悩まずに実装できますし、メンテナンスされない無駄なドキュメントを作らずに済みます。テストを書く工数はかかりますが、Laravelはこのあたりも洗練されていますので、簡単に書くことができてすばらしいですね。