質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
86.02%
Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

PHPUnit

PHPUnitは、PHP向けのユニット・テスト向けフレームワークで、手動では手間のかかるテスト作業を自動化し、繰り返し実行することが可能です。

Q&A

解決済

Laravel9+pgSQL+PHP Unitで、ほぼ同内容関数の2回目実行時だけ外部キー制約エラーがでる

iai
iai

総合スコア2

Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

PHPUnit

PHPUnitは、PHP向けのユニット・テスト向けフレームワークで、手動では手間のかかるテスト作業を自動化し、繰り返し実行することが可能です。

1回答

0グッド

0クリップ

283閲覧

投稿2022/12/02 03:55

編集2022/12/02 03:56

前提

Docker+Laravel9(Laravel sail 8.1)+postgresQL14+Inertia0.6.3+Vue3
認証系にLaravel Breezeを使用(usersテーブルはカスタムしており、後述の通り外部キー制約を含む)

上記の設定で、WindowsにてWebアプリを開発しております。
PHP Unitテストを実行するとエラーが出てしまい、色々試しましたが解決できません。

実現したいこと

以下のエラーが出ないようにしたいです。

発生している問題・エラーメッセージ

There was 1 error: 1) Tests\Feature\Auth\AuthenticationTest::test_users_can_not_authenticate_with_invalid_password Illuminate\Database\QueryException: SQLSTATE[23503]: Foreign key violation: 7 ERROR: insert or update on table "users" violates foreign key constraint "users_message_accept_type_id_foreign" DETAIL: Key (message_accept_type_id)=(1) is not present in table "message_accept_types". (SQL: insert into **(省略)**) (省略) Caused by PDOException: SQLSTATE[23503]: Foreign key violation: 7 ERROR: insert or update on table "users" violates foreign key constraint "users_message_accept_type_id_foreign" DETAIL: Key (message_accept_type_id)=(1) is not present in table "message_accept_types". (省略)

該当のソースコード

テストファイル

php

1<?php 2 3(省略) 4 5class AuthenticationTest extends TestCase 6{ 7 use RefreshDatabase; 8 9 public function test_users_can_authenticate_using_the_login_screen() 10 { 11 Schema::disableForeignKeyConstraints(); 12 $this->seed([ 13 MessageAcceptTypesTableSeeder::class, 14 ]); 15 $user = User::factory()->create(); 16 17 $response = $this->post('/login', [ 18 'email' => $user->email, 19 'password' => 'password' 20 ]); 21 22 $this->assertAuthenticated(); 23 $response->assertRedirect(RouteServiceProvider::HOME); 24 Schema::enableForeignKeyConstraints(); 25 } 26 27 public function test_users_can_not_authenticate_with_invalid_password() 28 { 29 Schema::disableForeignKeyConstraints(); 30 $this->seed([ 31 MessageAcceptTypesTableSeeder::class, 32 ]); 33 $user = User::factory()->create(); 34 35 $this->post('/login', [ 36 'email' => $user->email, 37 'password' => 'wrong-password' 38 ]); 39 40 $this->assertGuest(); 41 Schema::enableForeignKeyConstraints(); 42 } 43 44}

usersテーブル マイグレーションファイル抜粋

php

1 public function up() 2 { 3 // ユーザー情報テーブル 4 Schema::create('users', function (Blueprint $table) { 5 $table->id(); 6 $table->string('user_name'); 7 $table->string('email')->unique()->nullable(); 8 $table->timestamp('email_verified_at')->nullable(); 9 $table->string('password')->nullable(); 10 $table->bigInteger('message_accept_type_id'); 11 $table->foreign('message_accept_type_id')->references('id')->on('message_accept_types'); 12 (一部省略。message_accept_type_idの他に外部キー制約のあるカラムはありません) 13 $table->rememberToken(); 14 $table->timestamps(); 15 }); 16 }

message_accept_typesテーブル マイグレーションファイル抜粋

php

1public function up() 2 { 3 // メッセージ送信種別テーブル(Admin管理) 4 Schema::create('message_accept_types', function (Blueprint $table) { 5 $table->id(); 6 $table->string('message_accept_type_name'); 7 $table->timestamps(); 8 }); 9 }

message_accept_typesテーブル シーダーファイル

php

1(省略) 2 3class MessageAcceptTypesTableSeeder extends Seeder 4{ 5 /** 6 * Run the database seeds. 7 * 8 * @return void 9 */ 10 public function run() 11 {DB::table('message_accept_types')->insert([ 12 [ 13 'message_accept_type_name' => json_encode([ 14 'ja' => 'テスト1' 15 ]), 16 ], 17 [ 18 'message_accept_type_name' => json_encode([ 19 'ja' => 'テスト2' 20 ]), 21 ], 22 [ 23 'message_accept_type_name' => json_encode([ 24 'ja' => 'テスト3' 25 ]), 26 ], 27 ]); 28 29 } 30}

$sail php artisan migrate:fresh --seed 実行時、マイグレーション・シーダーともにすべて成功することを確認しています。

試したこと

関数を一部削除することで、エラーが出ずテストが成功することを確認しています。

失敗するコード↓

php

1(省略) 2 3class AuthenticationTest extends TestCase 4{ 5 use RefreshDatabase; 6 7 public function test_users_can_authenticate_using_the_login_screen() 8 { 9 Schema::disableForeignKeyConstraints(); 10 $this->seed([ 11 MessageAcceptTypesTableSeeder::class, 12 ]); 13 $user = User::factory()->create(); 14 15 $response = $this->post('/login', [ 16 'email' => $user->email, 17 'password' => 'password' 18 ]); 19 20 $this->assertAuthenticated(); 21 $response->assertRedirect(RouteServiceProvider::HOME); 22 Schema::enableForeignKeyConstraints(); 23 } 24 25 public function test_users_can_not_authenticate_with_invalid_password() 26 { 27 Schema::disableForeignKeyConstraints(); 28 $this->seed([ 29 MessageAcceptTypesTableSeeder::class, 30 ]); 31 $user = User::factory()->create(); 32 33 $this->post('/login', [ 34 'email' => $user->email, 35 'password' => 'wrong-password' 36 ]); 37 38 $this->assertGuest(); 39 Schema::enableForeignKeyConstraints(); 40 } 41 42} 43

成功するコード↓

※test_users_can_not_authenticate_with_invalid_passwordを削除しただけです。
逆にtest_users_can_authenticate_using_the_login_screenを削除した場合も成功します。

php

1(省略) 2 3class AuthenticationTest extends TestCase 4{ 5 use RefreshDatabase; 6 7 public function test_users_can_authenticate_using_the_login_screen() 8 { 9 Schema::disableForeignKeyConstraints(); 10 $this->seed([ 11 MessageAcceptTypesTableSeeder::class, 12 ]); 13 $user = User::factory()->create(); 14 15 $response = $this->post('/login', [ 16 'email' => $user->email, 17 'password' => 'password' 18 ]); 19 20 $this->assertAuthenticated(); 21 $response->assertRedirect(RouteServiceProvider::HOME); 22 Schema::enableForeignKeyConstraints(); 23 } 24 25} 26

ちなみに、全く同じ内容の関数を名前だけ変えて二回繰り返させた場合、やはり二回目だけ失敗します。

単純に書き方がおかしいだけなのか、pgSQL特有のキャッシュなど?が絡んでいるのか、もう半日くらい悩んでいてどうしようもないので質問させて頂きました。

お心当たりの方がいらっしゃいましたら、お知恵をお貸しください。
何卒宜しくお願い致します。

以下のような質問にはグッドを送りましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

グッドが多くついた質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

気になる質問をクリップする

クリップした質問は、後からいつでもマイページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

下記のような質問は推奨されていません。

  • 間違っている
  • 質問になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

適切な質問に修正を依頼しましょう。

回答1

0

ベストアンサー

手元で試したらpgsqlだけ動作がおかしい。
テスト2つのseed()の下に

php

1dump(MessageAcceptType::all()->pluck('id')->toArray());

を書くと分かるけど初回は1,2,3、次は4,5,6でidがリセットされてない。
SQLiteでは発生しないので「pgsqlで各テストに$this->seed()を書いた場合のみ」

対処方法。
MessageAcceptTypeは常に必要なら。
DatabaseSeeder

php

1 public function run() 2 { 3 $this->call(MessageAcceptTypesTableSeeder::class); 4 }

TestCaseに$seed追加。

php

1abstract class TestCase extends BaseTestCase 2{ 3 use CreatesApplication; 4 5 protected bool $seed = true; 6}

これで各テストではなくRefreshDatabase時にシードも実行。この場合はidがリセットされる。
https://laravel.com/docs/9.x/database-testing#running-seeders

各テストのほうはdisableForeignKeyConstraints()もseed()も不要なので消していい。

php

1 public function test_users_can_authenticate_using_the_login_screen() 2 { 3 $user = User::factory()->create(); 4 5 $response = $this->post('/login', [ 6 'email' => $user->email, 7 'password' => 'password' 8 ]); 9 10 $this->assertAuthenticated(); 11 $response->assertRedirect(RouteServiceProvider::HOME); 12 }

ついで。外部キーはこう書いたほうが間違えない。覚える価値はない部分なのでドキュメントからコピペして使いやすい書き方。

php

1$table->foreignId('message_accept_type_id')->constrained();

https://laravel.com/docs/9.x/migrations#foreign-key-constraints

投稿2022/12/03 02:29

kawax

総合スコア10002

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

下記のような回答は推奨されていません。

  • 間違っている回答
  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

回答へのコメント

iai

2022/12/03 02:44

手元で試して頂いたとのことで(しかもSQLiteのパターンまで…)、大変お手数をお掛けしました。 そして解決方法を丁寧にご教示頂き、本当にありがとうございます! お教えいただいた通りにコードを修正したところ、無事テストが通りました。 dumpを使用しての確認方法、外部キーの書き方も大変勉強になりました。 この度は誠にありがとうございました。

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
86.02%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問

同じタグがついた質問を見る

Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

PHPUnit

PHPUnitは、PHP向けのユニット・テスト向けフレームワークで、手動では手間のかかるテスト作業を自動化し、繰り返し実行することが可能です。