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?

実務1年目駆け出しエンジニアがLaravel&ReactでWebアプリケーション開発に挑戦してみた!(テスト・デバッグ編⑤)~Featureテスト2[コントローラー②/大学/学部/研究室]~

0
Posted at

実務1年目駆け出しエンジニアがLaravel&ReactでWebアプリケーション開発に挑戦してみた!(その47)

0. 初めに

Webアプリケーション開発についてゼロから解説しているシリーズで、今回でテスト編の第5回目です!

引き続きコントローラーのFeatureテストを行っていきます!

1. 大学コントローラー

まず、大学コントローラーについてのテストケースを作成します。

1.1 作成

実行コマンド

/var/www
$ php artisan make:test Controllers/UniversityControllerTest
\project-root\src\tests\Feature\Controllers\UniversityControllerTest.php
<?php

namespace Tests\Feature\Controllers;

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

class UniversityControllerTest extends TestCase
{
    use RefreshDatabase;

    // -------------------------
    // index
    // -------------------------

    public function test_大学一覧を表示できる(): void
    {
        // Arrange
        University::factory()->count(3)->create();

        // Act
        $response = $this->get(route('universities.index'));

        // Assert
        $response->assertOk();
        $response->assertInertia(fn ($page) => $page
            ->component('University/Index')
            ->has('universities.data', 3)
        );
    }

    public function test_キーワードで大学を検索できる(): void
    {
        // Arrange
        University::factory()->create(['name' => '東京大学']);
        University::factory()->create(['name' => '大阪大学']);

        // Act
        $response = $this->get(route('universities.index', ['query' => '東京']));

        // Assert
        $response->assertOk();
        $response->assertInertia(fn ($page) => $page
            ->component('University/Index')
            ->has('universities.data', 1)
            ->where('query', '東京')
        );
    }

    // -------------------------
    // store
    // -------------------------

    public function test_認証済みユーザーが大学を作成できる(): void
    {
        // Arrange
        $user = User::factory()->create();

        // Act
        $response = $this->actingAs($user)
            ->post(route('universities.store'), [
                'name' => 'テスト大学',
                'type' => 'national',
            ]);

        // Assert
        $response->assertRedirect();
        $this->assertDatabaseHas('universities', [
            'name' => 'テスト大学',
            'type' => 'national',
            'created_by' => $user->id,
        ]);
    }

    public function test_未認証ユーザーは大学を作成できない(): void
    {
        // Act
        $response = $this->post(route('universities.store'), [
            'name' => 'テスト大学',
            'type' => 'national',
        ]);

        // Assert
        $response->assertRedirect('/');
    }

    public function test_バリデーションエラーで大学を作成できない(): void
    {
        // Arrange
        $user = User::factory()->create();

        // Act(name が空)
        $response = $this->actingAs($user)
            ->post(route('universities.store'), [
                'name' => '',
                'type' => 'national',
            ]);

        // Assert
        $response->assertSessionHasErrors('name');
        $this->assertDatabaseMissing('universities', ['type' => 'national']);
    }

    public function test_重複した大学名では作成できない(): void
    {
        // Arrange
        $user = User::factory()->create();
        University::factory()->create(['name' => 'テスト大学']);

        // Act
        $response = $this->actingAs($user)
            ->post(route('universities.store'), [
                'name' => 'テスト大学',
                'type' => 'national',
            ]);

        // Assert
        $response->assertSessionHasErrors('name');
        $this->assertDatabaseCount('universities', 1);
    }

    // -------------------------
    // update
    // -------------------------

    public function test_認証済みユーザーが大学情報を更新できる(): void
    {
        // Arrange
        $user = User::factory()->create();
        $university = University::factory()->create([
            'name' => '更新前大学',
            'type' => 'national',
            'version' => 1,
        ]);

        // Act
        $response = $this->actingAs($user)
            ->put(route('universities.update', $university), [
                'name' => '更新後大学',
                'type' => 'public',
                'comment' => '名称変更のため更新',
                'version' => 1,
            ]);

        // Assert
        $response->assertRedirect();
        $this->assertDatabaseHas('universities', [
            'id' => $university->id,
            'name' => '更新後大学',
            'type' => 'public',
            'version' => 2,
        ]);
    }

    public function test_バージョン不一致の場合は更新できない(): void
    {
        // Arrange
        $user = User::factory()->create();
        $university = University::factory()->create([
            'name' => '更新前大学',
            'version' => 2,
        ]);

        // Act(古いバージョン番号を送信)
        $response = $this->actingAs($user)
            ->put(route('universities.update', $university), [
                'name' => '更新後大学',
                'type' => 'national',
                'comment' => '変更理由',
                'version' => 1,
            ]);

        // Assert
        $response->assertSessionHasErrors('version');
        $this->assertDatabaseHas('universities', [
            'id' => $university->id,
            'name' => '更新前大学',
        ]);
    }

    // -------------------------
    // history
    // -------------------------

    public function test_大学の編集履歴を表示できる(): void
    {
        // Arrange
        $user = User::factory()->create();
        $university = University::factory()->create();
        $university->users()->attach($user->id, [
            'comment' => '初回登録',
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        // Act
        $response = $this->get(route('universities.history', $university));

        // Assert
        $response->assertOk();
        $response->assertInertia(fn ($page) => $page
            ->component('University/History')
            ->has('editHistory', 1)
        );
    }
}

ポイントは二つです。

一つ目はindexメソッドについてです。
ここでは、assertInertiaというInertia.js限定のアサーションを使うことができます!

通常のLaravelだけですと、リダイレクトのテストしかできませんが、これを用いることで、Reactのどのコンポーネントについてかまでテストすることができます!

historyメソッドに関しても同様です。

二つ目は、updateメソッドです。

これは、同じ大学を複数のユーザーが同時に編集するようなことを防ぐために、実装したバージョン管理の機能(楽観的ロック)をテストしています。

現在のDBに登録されているバージョンと取得済みのバージョンが違う場合は上書きされないようにロックしていたはずです。

1.2 実行

実行コマンド

/var/www
$ php artisan test --filter=UniversityControllerTest

実行結果
image.png

assertInertiaを使うためにはJavaScriptファイルにアクセスために簡易サーバーを立ち上げていないといけないため、別ターミナルで以下のコマンドをあらかじめ実行しておく必要があります。

実行コマンド

/var/www
$ npm run dev

2. 学部コントローラー

次は、学部コントローラーです。
大学とほぼ同じです。

実行コマンド

/var/www
$ php artisan make:test Controllers/FacultyControllerTest
\project-root\src\tests\Feature\Controllers\FacultyControllerTest.php
<?php

namespace Tests\Feature\Controllers;

use App\Models\Faculty;
use App\Models\University;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class FacultyControllerTest extends TestCase
{
    use RefreshDatabase;

    // -------------------------
    // index
    // -------------------------

    public function test_学部一覧を表示できる(): void
    {
        // Arrange
        $university = University::factory()->create();
        Faculty::factory()->count(3)->create(['university_id' => $university->id]);

        // Act
        $response = $this->get(route('faculties.index', $university));

        // Assert
        $response->assertOk();
        $response->assertInertia(fn ($page) => $page
            ->component('Faculty/Index')
            ->has('faculties', 3)
            ->where('university.id', $university->id)
        );
    }

    // -------------------------
    // store
    // -------------------------

    public function test_認証済みユーザーが学部を作成できる(): void
    {
        // Arrange
        $user = User::factory()->create();
        $university = University::factory()->create();

        // Act
        $response = $this->actingAs($user)
            ->post(route('faculties.store', $university), [
                'name' => 'テスト学部',
            ]);

        // Assert
        $response->assertRedirect();
        $this->assertDatabaseHas('faculties', [
            'name' => 'テスト学部',
            'university_id' => $university->id,
            'created_by' => $user->id,
        ]);
    }

    public function test_未認証ユーザーは学部を作成できない(): void
    {
        // Arrange
        $university = University::factory()->create();

        // Act
        $response = $this->post(route('faculties.store', $university), [
            'name' => 'テスト学部',
        ]);

        // Assert
        $response->assertRedirect('/');
    }

    public function test_バリデーションエラーで学部を作成できない(): void
    {
        // Arrange
        $user = User::factory()->create();
        $university = University::factory()->create();

        // Act(name が空)
        $response = $this->actingAs($user)
            ->post(route('faculties.store', $university), [
                'name' => '',
            ]);

        // Assert
        $response->assertSessionHasErrors('name');
        $this->assertDatabaseMissing('faculties', ['university_id' => $university->id]);
    }

    public function test_同じ大学内で重複した学部名では作成できない(): void
    {
        // Arrange
        $user = User::factory()->create();
        $university = University::factory()->create();
        Faculty::factory()->create([
            'name' => 'テスト学部',
            'university_id' => $university->id,
        ]);

        // Act
        $response = $this->actingAs($user)
            ->post(route('faculties.store', $university), [
                'name' => 'テスト学部',
            ]);

        // Assert
        $response->assertSessionHasErrors('name');
        $this->assertDatabaseCount('faculties', 1);
    }

    // -------------------------
    // update
    // -------------------------

    public function test_認証済みユーザーが学部情報を更新できる(): void
    {
        // Arrange
        $user = User::factory()->create();
        $faculty = Faculty::factory()->create([
            'name' => '更新前学部',
            'version' => 1,
        ]);

        // Act
        $response = $this->actingAs($user)
            ->put(route('faculties.update', $faculty), [
                'name' => '更新後学部',
                'comment' => '名称変更のため更新',
                'version' => 1,
            ]);

        // Assert
        $response->assertRedirect();
        $this->assertDatabaseHas('faculties', [
            'id' => $faculty->id,
            'name' => '更新後学部',
            'version' => 2,
        ]);
    }

    public function test_バージョン不一致の場合は更新できない(): void
    {
        // Arrange
        $user = User::factory()->create();
        $faculty = Faculty::factory()->create([
            'name' => '更新前学部',
            'version' => 2,
        ]);

        // Act(古いバージョン番号を送信)
        $response = $this->actingAs($user)
            ->put(route('faculties.update', $faculty), [
                'name' => '更新後学部',
                'comment' => '変更理由',
                'version' => 1,
            ]);

        // Assert
        $response->assertSessionHasErrors('version');
        $this->assertDatabaseHas('faculties', [
            'id' => $faculty->id,
            'name' => '更新前学部',
        ]);
    }

    // -------------------------
    // history
    // -------------------------

    public function test_学部の編集履歴を表示できる(): void
    {
        // Arrange
        $user = User::factory()->create();
        $faculty = Faculty::factory()->create();
        $faculty->users()->attach($user->id, [
            'comment' => '初回登録',
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        // Act
        $response = $this->get(route('faculties.history', $faculty));

        // Assert
        $response->assertOk();
        $response->assertInertia(fn ($page) => $page
            ->component('Faculty/History')
            ->has('editHistory', 1)
        );
    }
}


実行コマンド

/var/www
$ php artisan test --filter=FacultyControllerTest

実行結果
image.png

3. 研究室コントローラー

最後は、研究室です。

実行コマンド

/var/www
$ php artisan make:test Controllers/LabControllerTest
\project-root\src\tests\Feature\Controllers\LabControllerTest.php
<?php

namespace Tests\Feature\Controllers;

use App\Models\Faculty;
use App\Models\Lab;
use App\Models\Review;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class LabControllerTest extends TestCase
{
    use RefreshDatabase;

    // -------------------------
    // show
    // -------------------------

    public function test_研究室の詳細を表示できる(): void
    {
        // Arrange
        $lab = Lab::factory()->create();

        // Act
        $response = $this->get(route('labs.show', $lab));

        // Assert
        $response->assertOk();
        $response->assertInertia(fn ($page) => $page
            ->component('Lab/Show')
            ->where('lab.id', $lab->id)
        );
    }

    public function test_研究室の詳細にレビュー一覧が含まれる(): void
    {
        // Arrange
        $lab = Lab::factory()->create();
        Review::factory()->count(2)->create(['lab_id' => $lab->id]);

        // Act
        $response = $this->get(route('labs.show', $lab));

        // Assert
        $response->assertOk();
        $response->assertInertia(fn ($page) => $page
            ->component('Lab/Show')
            ->has('lab.reviews', 2)
        );
    }

    // -------------------------
    // index
    // -------------------------

    public function test_研究室一覧を表示できる(): void
    {
        // Arrange
        $faculty = Faculty::factory()->create();
        Lab::factory()->count(3)->create(['faculty_id' => $faculty->id]);

        // Act
        $response = $this->get(route('labs.index', $faculty));

        // Assert
        $response->assertOk();
        $response->assertInertia(fn ($page) => $page
            ->component('Lab/Index')
            ->has('labs.data', 3)
            ->where('faculty.id', $faculty->id)
        );
    }

    public function test_ソート条件を指定して研究室一覧を表示できる(): void
    {
        // Arrange
        $faculty = Faculty::factory()->create();
        Lab::factory()->count(2)->create(['faculty_id' => $faculty->id]);

        // Act
        $response = $this->get(route('labs.index', ['faculty' => $faculty, 'sort' => 'reviews_count']));

        // Assert
        $response->assertOk();
        $response->assertInertia(fn ($page) => $page
            ->component('Lab/Index')
            ->where('sort', 'reviews_count')
        );
    }

    // -------------------------
    // store
    // -------------------------

    public function test_認証済みユーザーが研究室を作成できる(): void
    {
        // Arrange
        $user = User::factory()->create();
        $faculty = Faculty::factory()->create();

        // Act
        $response = $this->actingAs($user)
            ->post(route('labs.store', $faculty), [
                'name' => 'テスト研究室',
                'description' => null,
                'url' => null,
                'professor_name' => null,
                'professor_url' => null,
                'gender_ratio_male' => 6,
                'gender_ratio_female' => 4,
            ]);

        // Assert
        $response->assertRedirect(route('labs.show', Lab::first()));
        $this->assertDatabaseHas('labs', [
            'name' => 'テスト研究室',
            'faculty_id' => $faculty->id,
            'created_by' => $user->id,
            'gender_ratio_male' => 6,
            'gender_ratio_female' => 4,
        ]);
    }

    public function test_未認証ユーザーは研究室を作成できない(): void
    {
        // Arrange
        $faculty = Faculty::factory()->create();

        // Act
        $response = $this->post(route('labs.store', $faculty), [
            'name' => 'テスト研究室',
            'gender_ratio_male' => 6,
            'gender_ratio_female' => 4,
        ]);

        // Assert
        $response->assertRedirect('/');
    }

    public function test_バリデーションエラーで研究室を作成できない(): void
    {
        // Arrange
        $user = User::factory()->create();
        $faculty = Faculty::factory()->create();

        // Act(name が空)
        $response = $this->actingAs($user)
            ->post(route('labs.store', $faculty), [
                'name' => '',
                'gender_ratio_male' => 6,
                'gender_ratio_female' => 4,
            ]);

        // Assert
        $response->assertSessionHasErrors('name');
        $this->assertDatabaseMissing('labs', ['faculty_id' => $faculty->id]);
    }

    public function test_男女比の合計が10を超えると研究室を作成できない(): void
    {
        // Arrange
        $user = User::factory()->create();
        $faculty = Faculty::factory()->create();

        // Act(合計 11 を送信)
        $response = $this->actingAs($user)
            ->post(route('labs.store', $faculty), [
                'name' => 'テスト研究室',
                'gender_ratio_male' => 6,
                'gender_ratio_female' => 5,
            ]);

        // Assert
        $response->assertSessionHasErrors('gender_ratio_female');
        $this->assertDatabaseMissing('labs', ['faculty_id' => $faculty->id]);
    }

    public function test_同じ学部内で重複した研究室名では作成できない(): void
    {
        // Arrange
        $user = User::factory()->create();
        $faculty = Faculty::factory()->create();
        Lab::factory()->create([
            'name' => 'テスト研究室',
            'faculty_id' => $faculty->id,
        ]);

        // Act
        $response = $this->actingAs($user)
            ->post(route('labs.store', $faculty), [
                'name' => 'テスト研究室',
                'gender_ratio_male' => 6,
                'gender_ratio_female' => 4,
            ]);

        // Assert
        $response->assertSessionHasErrors('name');
        $this->assertDatabaseCount('labs', 1);
    }

    // -------------------------
    // update
    // -------------------------

    public function test_認証済みユーザーが研究室情報を更新できる(): void
    {
        // Arrange
        $user = User::factory()->create();
        $lab = Lab::factory()->create([
            'name' => '更新前研究室',
            'version' => 1,
        ]);

        // Act
        $response = $this->actingAs($user)
            ->put(route('labs.update', $lab), [
                'name' => '更新後研究室',
                'description' => null,
                'url' => null,
                'professor_name' => null,
                'professor_url' => null,
                'gender_ratio_male' => 6,
                'gender_ratio_female' => 4,
                'comment' => '名称変更のため更新',
                'version' => 1,
            ]);

        // Assert
        $response->assertRedirect(route('labs.show', $lab));
        $this->assertDatabaseHas('labs', [
            'id' => $lab->id,
            'name' => '更新後研究室',
            'version' => 2,
        ]);
    }

    public function test_バージョン不一致の場合は更新できない(): void
    {
        // Arrange
        $user = User::factory()->create();
        $lab = Lab::factory()->create([
            'name' => '更新前研究室',
            'version' => 2,
        ]);

        // Act(古いバージョン番号を送信)
        $response = $this->actingAs($user)
            ->put(route('labs.update', $lab), [
                'name' => '更新後研究室',
                'description' => null,
                'url' => null,
                'professor_name' => null,
                'professor_url' => null,
                'gender_ratio_male' => 6,
                'gender_ratio_female' => 4,
                'comment' => '変更理由',
                'version' => 1,
            ]);

        // Assert
        $response->assertSessionHasErrors('version');
        $this->assertDatabaseHas('labs', [
            'id' => $lab->id,
            'name' => '更新前研究室',
        ]);
    }

    // -------------------------
    // history
    // -------------------------

    public function test_研究室の編集履歴を表示できる(): void
    {
        // Arrange
        $user = User::factory()->create();
        $lab = Lab::factory()->create();
        $lab->users()->attach($user->id, [
            'comment' => '初回登録',
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        // Act
        $response = $this->get(route('labs.history', $lab));

        // Assert
        $response->assertOk();
        $response->assertInertia(fn ($page) => $page
            ->component('Lab/History')
            ->has('editHistory', 1)
        );
    }
}

保存処理がやや複雑ですが、基本的には大学・学部と考え方は一緒です。

また、前回レビューコントローラーを作った際にはできなかった、レビューの一覧表示もshowセクションの中で行っています。

実行コマンド

/var/www
$ php artisan test --filter=LabControllerTest

実行結果
image.png

4. まとめ・次回予告

今日は、Featureテスト二日目ということで、大学・学部・研究室コントローラーについてのテストケースを作成しました!

次回は、残りのコントローラーに関してのテストケースを作成し行きましょう!

これまでの記事一覧

☆要件定義・設計編

☆環境構築編

☆バックエンド実装編

☆フロントエンド実装編

☆テスト・デバッグ編

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?