0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

laravelでPHPUnitを使ってテストをする

Posted at

前書き

今回は作成中のポートフォリオの処理にミスがないか、確認するためのテストの作成を行いました。

環境

上記記事に準じて環境構築を行っています、

実現したいこと

・各コントローラーの処理のテストをしたい。
→リソースコントローラにあたる7つのメソッド(index,edit,create,show,store,delete,update)と画像登録のテスト

対応方法

①事前設定

・新しくテストを作成します

php artisan make:test テスト名

--unitコマンドをつけると、tests/Unitディレクトリに、
つけないとtests/Featureディレクトリにテストが作成されます。

Featureテストは、小さい単位(今回は各コントローラのメソッド)が対象になります。
Unitテストは、アプリケーションを利用しないため、Eloquentのメソッドが使用できない等の制約がありますが、テストの実行が軽いです。

モデル名を使ってテストを作成すると自動で紐付けされましたが、そうでない場合は、テスト内にuse モデル名を記載し連携させる必要があります。

今回はFeatureテストを作成していきます。

②テスト用DBの作成
今回はdockerで環境構築しているので、テスト用DBコンテナの作成を実施します。下記記事を参照させていただきました。とても分かり易かったです、

docker-compose.yml
 db-test:
    build: ./infra/mysql-test
    volumes:
      - db-store:/db-test/var/lib/mysql
    ports:
      - 3000:3306

※インデントがずれるとエラーが出るので注意

infra/mysql-test/dockerfile
FROM mysql/mysql-server:8.0

ENV MYSQL_DATABASE=laravel_local_test \
  MYSQL_USER=phper \
  MYSQL_PASSWORD=secret \
  MYSQL_ROOT_PASSWORD=secret \
  TZ=Asia/Tokyo

元記事では設定ファイル(.my.cnf)が入っていましたが、今回は省略しています。

.env.testingファイル(テスト用の環境変数を記載)を作成します。
phpunit.xmlの内容に合うよう、既存の.env.exampleファイルをコピーして編集します。

.env.testing
APP_ENV=testing
APP_KEY=

DB_CONNECTION=mysql
DB_HOST=db-test
DB_PORT=3306
DB_DATABASE=laravel_local_test
DB_USERNAME=phper
DB_PASSWORD=secret
php artisan key:generate --env=testing

テスト用DBをmigrateします。

docker compose exec app php artisan migrate --env=testing

--env=testingコマンドをつけることで、.env.testingの環境変数を参照してDBを作成します。

③テストコードの作成
今回は例としてArticleControllerを対象にします。
Factoryの記載は割愛します。

create_articles_table
  public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->foreignId('user_id')->constrained('users')->onDelete('cascade');
            $table->foreignId('radio_id')->constrained('radios')->onDelete('cascade');
            $table->date('radio_date');
            $table->text('body');
            $table->text('link')->nullable();
            $table->timestamps();
        });
    }
tests/Feature/ArticleTest.php
<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
use App\Models\Article;
use App\Models\User;

class ArticleTest extends TestCase
{
    /**
     * A basic feature test example.
     *
     * @return void
     */

    use RefreshDatabase;

    public function setUp(): void
    {
        parent::setUp();

        $this->article = Article::factory()->create();

        $this->user = User::factory()->create();

        $this->data = ([
            'user_id' => $this->article->user_id,
            'radio_id' => $this->article->radio_id,
            'radio_date' => $this->article->radio_date,
            'body' => $this->article->body,
            'link' => $this->article->link,
        ]);
    }

    //index
    public function test_index()
    {
        $response = $this->actingAs($this->user)
            ->get('/articles');
        $response->assertStatus(200);
    }

    //create
    public function test_create()
    {
        $response = $this->actingAs($this->user)
            ->get('/articles/create');
        $response->assertStatus(200)
            ->assertViewHas('radios');
    }

    //store
    public function test_store()
    {
        $response = $this->actingAs($this->user)
            ->post('/articles', $this->data);
        $response->assertRedirect('/articles');
        $this->assertDatabaseHas('articles', [
            'user_id' => $this->article->user_id,
            'radio_id' => $this->article->radio_id,
            'radio_date' => $this->article->radio_date,
            'body' => $this->article->body,
            'link' => $this->article->link,
        ]);
    }
    
    //edit
    public function test_edit()
    {
        $response = $this->actingAs($this->article->user)
            ->from('/articles/' . $this->article->id)
            ->get('/articles/' . $this->article->id . '/edit');
        $response->assertStatus(200)
            ->assertViewHas('radios');
    }

    //update
    public function test_update()
    {
        $response = $this->actingAs($this->article->user)
            ->from('/articles/' . $this->article->id . '/edit')
            ->put(
                '/articles/' . $this->article->id,
                [
                    'user_id' => $this->article->user_id,
                    'radio_id' => $this->article->radio_id,
                    'radio_date' => $this->article->radio_date,
                    'body' => 'aaa',

                ]
            );
        $response->assertRedirect('/articles');
        $this->assertDatabaseHas('articles', [
            'body' => 'aaa',
        ]);
    }

    //destroy
    public function test_destroy()
    {
        $response = $this->actingAs($this->article->user)
            ->delete('/articles/' . $this->article->id);
        $response->assertRedirect('/articles');
        $this->assertDatabaseMissing('articles', $this->data)
            ->assertEquals(0, Article::count());
    }
}

use RefreshDatabase;

テストを実行する度に、migrate:refreshされます。

$this->actingAs(___)

actingAs内に取る引数は、

登録済みユーザーは、$this->user、

投稿者のみ権限がある場合は、$this->article->user

画像投稿

ArticleTest
    public function test_image_store()
    {
        $image = HttpUploadedFile::fake()
            ->image('hoge.png');

        $response = $this->actingAs($this->user)
            ->post('/articles', [
                'image' => $image,
            ]);
        $response->assertRedirect('/articles');
        $this->assertDatabaseHas('articles', [
            'image' => 'hoge.png',
        ]);
    }

    public function test_image_update()
    {
        $image = HttpUploadedFile::fake()
            ->image('hogehoge.png');

        $response = $this->actingAs($this->user)
            ->from('/articles/' . $this->article->id)
            ->put('/articles/' . $this->article->id, [
                'image' => $image,
            ]);
        $response->assertRedirect('/articles/' . $this->article->id);
        $this->assertDatabaseHas('articles', [
            'image' => 'hogehoge.png',
        ]);
    }

$image = HttpUploadedFile::fake()

テスト用のダミー画像を作成します。

④テスト実行

docker compose exec app php artisan test

もしくは

./vendor/bin/phpunit

最後に

より良いテストコードの記述方法や投稿内容に誤りがありましたら、ご指摘いただけると助かります。

今後も学習内容の備忘録として投稿していきたいと思います。

また、自動テスト(CI/CD)についても調べて、今後実装していきたいです。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?