نسخه:

تست پایگاه داده

معرفی

لاراول انواع ابزارها و ادعاهای مفیدی را ارائه می دهد تا آزمایش برنامه های کاربردی مبتنی بر پایگاه داده شما را آسان تر کند. علاوه بر این، کارخانه‌ها و کاشت‌های مدل لاراول، ایجاد رکوردهای پایگاه داده آزمایشی را با استفاده از مدل‌ها و روابط Eloquent برنامه شما بدون دردسر می‌سازند. در مستندات زیر به همه این ویژگی های قدرتمند خواهیم پرداخت.

تنظیم مجدد پایگاه داده پس از هر تست

قبل از اینکه خیلی بیشتر ادامه دهیم، بیایید در مورد چگونگی تنظیم مجدد پایگاه داده خود پس از هر یک از آزمایشات خود بحث کنیم تا داده های آزمایش قبلی با آزمایش های بعدی تداخل نداشته باشند. ویژگی گنجانده شده لاراول Illuminate\Foundation\Testing\RefreshDatabase از این موضوع برای شما مراقبت خواهد کرد. به سادگی از این ویژگی در کلاس آزمایشی خود استفاده کنید:

<?php
 
namespace Tests\Feature;
 
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Tests\TestCase;
 
class ExampleTest extends TestCase
{
use RefreshDatabase;
 
/**
* A basic functional test example.
*
* @return void
*/
public function test_basic_example()
{
$response = $this->get('/');
 
// ...
}
}

تعریف کارخانه های مدل

نمای کلی مفهوم

ابتدا اجازه دهید در مورد کارخانه های مدل Eloquent صحبت کنیم. هنگام آزمایش، ممکن است لازم باشد قبل از اجرای آزمایش، چند رکورد را در پایگاه داده خود وارد کنید. به جای تعیین دستی مقدار هر ستون هنگام ایجاد این داده های آزمایشی، لاراول به شما اجازه می دهد مجموعه ای از ویژگی های پیش فرض را برای هر یک از مدل های Eloquent خود با استفاده از کارخانه های مدل تعریف کنید.

برای مشاهده نمونه ای از نحوه نوشتن یک کارخانه، به database/factories/UserFactory.php فایل موجود در برنامه خود نگاهی بیندازید. این کارخانه با تمام برنامه های جدید لاراول گنجانده شده است و شامل تعریف کارخانه زیر است:

namespace Database\Factories;
 
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
 
class UserFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'name' => $this->faker->name(),
'email' => $this->faker->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
];
}
}

همانطور که می بینید، کارخانه ها در ابتدایی ترین شکل خود، کلاس هایی هستند که کلاس کارخانه پایه لاراول را گسترش می دهند و definition متد را تعریف می کنند. این definition متد مجموعه پیش‌فرض مقادیر مشخصه‌ای را که باید هنگام ایجاد یک مدل با استفاده از کارخانه اعمال شوند، برمی‌گرداند.

از طریق ویژگی، کارخانه ها به کتابخانه Faker faker PHP دسترسی دارند ، که به شما امکان می دهد انواع مختلفی از داده های تصادفی را برای آزمایش به راحتی تولید کنید.

faker_locale می‌توانید با افزودن گزینه‌ای به config/app.php فایل پیکربندی، منطقه Faker برنامه خود را تنظیم کنید .

کارخانه های تولید کننده

برای ایجاد یک کارخانه، make:factory دستور Artisan را اجرا کنید :

php artisan make:factory PostFactory

کلاس کارخانه جدید در database/factories دایرکتوری شما قرار می گیرد.

کنوانسیون های کشف مدل و کارخانه

هنگامی که کارخانه‌های خود را تعریف کردید، می‌توانید از factory روش استاتیکی که توسط این ویژگی برای مدل‌های خود ارائه می‌شود، استفاده کنید Illuminate\Database\Eloquent\Factories\HasFactory تا نمونه‌ای کارخانه‌ای برای آن مدل ایجاد کنید.

روش این HasFactory صفت factory از قراردادها برای تعیین کارخانه مناسب برای مدلی که صفت به آن اختصاص داده شده است استفاده می کند. به طور خاص، متد به دنبال کارخانه ای در Database\Factories فضای نام می گردد که نام کلاسی مطابق با نام مدل داشته باشد و پسوند آن باشد Factory . اگر این قراردادها برای برنامه یا کارخانه خاص شما اعمال نمی شود، می توانید newFactory روش را روی مدل خود بازنویسی کنید تا نمونه ای از کارخانه مربوطه مدل را مستقیماً بازگردانید:

use Database\Factories\Administration\FlightFactory;
 
/**
* Create a new factory instance for the model.
*
* @return \Illuminate\Database\Eloquent\Factories\Factory
*/
protected static function newFactory()
{
return FlightFactory::new();
}

در مرحله بعد، یک model ویژگی در کارخانه مربوطه تعریف کنید:

use App\Administration\Flight;
use Illuminate\Database\Eloquent\Factories\Factory;
 
class FlightFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Flight::class;
}

ایالات کارخانه

روش‌های دستکاری حالت به شما امکان می‌دهد تا تغییرات گسسته‌ای را تعریف کنید که می‌توانند در هر ترکیبی در کارخانه‌های مدل شما اعمال شوند. به عنوان مثال، Database\Factories\UserFactory کارخانه شما ممکن است حاوی یک suspended روش حالت باشد که یکی از مقادیر مشخصه پیش‌فرض آن را تغییر می‌دهد.

روش های تبدیل حالت معمولاً state روش ارائه شده توسط کلاس کارخانه پایه لاراول را می نامند. این state روش یک بسته را می پذیرد که آرایه ای از ویژگی های خام تعریف شده برای کارخانه را دریافت می کند و باید آرایه ای از ویژگی ها را برای اصلاح برگرداند:

/**
* Indicate that the user is suspended.
*
* @return \Illuminate\Database\Eloquent\Factories\Factory
*/
public function suspended()
{
return $this->state(function (array $attributes) {
return [
'account_status' => 'suspended',
];
});
}

تماس های کارخانه ای

afterMaking تماس‌های کارخانه با استفاده از روش‌های و ثبت می‌شوند afterCreating و به شما اجازه می‌دهند تا پس از ساخت یا ایجاد یک مدل، کارهای بیشتری را انجام دهید. شما باید این کال بک ها را با تعریف configure متدی در کلاس کارخانه خود ثبت کنید. این متد به طور خودکار توسط لاراول زمانی که کارخانه نمونه سازی می شود فراخوانی می شود:

namespace Database\Factories;
 
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
 
class UserFactory extends Factory
{
/**
* Configure the model factory.
*
* @return $this
*/
public function configure()
{
return $this->afterMaking(function (User $user) {
//
})->afterCreating(function (User $user) {
//
});
}
 
// ...
}

ایجاد مدل ها با استفاده از کارخانه ها

نمونه سازی مدل ها

هنگامی که کارخانه‌های خود را تعریف کردید، می‌توانید از factory روش استاتیکی که توسط این ویژگی برای مدل‌های خود ارائه می‌شود، استفاده کنید Illuminate\Database\Eloquent\Factories\HasFactory تا نمونه‌ای کارخانه‌ای برای آن مدل ایجاد کنید. بیایید به چند نمونه از ایجاد مدل نگاهی بیندازیم. make ابتدا از روشی برای ایجاد مدل‌ها بدون ماندگاری در پایگاه داده استفاده می‌کنیم :

use App\Models\User;
 
public function test_models_can_be_instantiated()
{
$user = User::factory()->make();
 
// Use model in tests...
}

می توانید با استفاده از count روش زیر مجموعه ای از مدل های زیادی ایجاد کنید:

$users = User::factory()->count(3)->make();

کشورهای متقاضی

همچنین می توانید هر یک از حالت های خود را در مدل ها اعمال کنید. اگر می خواهید چندین تبدیل حالت را در مدل ها اعمال کنید، می توانید به سادگی روش های تبدیل حالت را مستقیماً فراخوانی کنید:

$users = User::factory()->count(5)->suspended()->make();

ویژگی های برتر

اگر می‌خواهید برخی از مقادیر پیش‌فرض مدل‌های خود را لغو کنید، می‌توانید آرایه‌ای از مقادیر را به make متد ارسال کنید. فقط ویژگی‌های مشخص شده جایگزین می‌شوند در حالی که بقیه ویژگی‌ها روی مقادیر پیش‌فرض خود که توسط کارخانه مشخص شده است، تنظیم می‌شوند:

$user = User::factory()->make([
'name' => 'Abigail Otwell',
]);

روش دیگر، state ممکن است به طور مستقیم در نمونه کارخانه برای انجام یک تبدیل حالت درون خطی فراخوانی شود:

$user = User::factory()->state([
'name' => 'Abigail Otwell',
])->make();

مدل های ماندگار

این create روش نمونه‌های مدل را نمونه‌سازی می‌کند و آنها را با استفاده از روش Eloquent در پایگاه داده نگهداری می‌کند save :

use App\Models\User;
 
public function test_models_can_be_persisted()
{
// Create a single App\Models\User instance...
$user = User::factory()->create();
 
// Create three App\Models\User instances...
$users = User::factory()->count(3)->create();
 
// Use model in tests...
}

می‌توانید ویژگی‌های مدل پیش‌فرض کارخانه را با ارسال آرایه‌ای از ویژگی‌ها به create متد لغو کنید:

$user = User::factory()->create([
'name' => 'Abigail',
]);

دنباله ها

گاهی اوقات ممکن است بخواهید مقدار یک ویژگی مدل معین را برای هر مدل ایجاد شده جایگزین کنید. شما می توانید این کار را با تعریف تبدیل حالت به عنوان یک دنباله انجام دهید. به عنوان مثال، ممکن است بخواهید مقدار یک admin ستون را بین Y و N برای هر کاربر ایجاد شده جایگزین کنید:

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Sequence;
 
$users = User::factory()
->count(10)
->state(new Sequence(
['admin' => 'Y'],
['admin' => 'N'],
))
->create();

در این مثال، پنج کاربر با admin مقدار Y و پنج کاربر با admin مقدار ایجاد خواهند شد N .

در صورت لزوم، می توانید یک بسته را به عنوان مقدار توالی اضافه کنید. هر بار که دنباله به یک مقدار جدید نیاز دارد، بسته شدن فراخوانی می شود:

$users = User::factory()
->count(10)
->state(new Sequence(
fn ($sequence) => ['role' => UserRoles::all()->random()],
))
->create();

در بسته شدن توالی، می‌توانید به $index یا $count ویژگی‌های نمونه دنباله‌ای که به بسته تزریق می‌شود دسترسی داشته باشید. این $index ویژگی شامل تعداد تکرارها از طریق دنباله ای است که تاکنون رخ داده است، در حالی که $count ویژگی شامل تعداد کل دفعاتی است که دنباله فراخوانی می شود:

$users = User::factory()
->count(10)
->sequence(fn ($sequence) => ['name' => 'Name '.$sequence->index])
->create();

روابط کارخانه

روابط زیادی دارد

سپس، بیایید ایجاد روابط مدل Eloquent را با استفاده از روش‌های کارخانه روان لاراول بررسی کنیم. ابتدا فرض کنید برنامه ما دارای یک App\Models\User مدل و یک App\Models\Post مدل است. همچنین، فرض کنیم که User مدل hasMany رابطه ای را با Post . has ما می توانیم با استفاده از روش ارائه شده توسط کارخانه های لاراول، کاربری ایجاد کنیم که دارای سه پست باشد . این has روش یک نمونه کارخانه را می پذیرد:

use App\Models\Post;
use App\Models\User;
 
$user = User::factory()
->has(Post::factory()->count(3))
->create();

طبق قرارداد، هنگام انتقال یک Post مدل به has روش، لاراول فرض می‌کند که User مدل باید posts متدی داشته باشد که رابطه را تعریف کند. در صورت لزوم، می توانید به صراحت نام رابطه ای را که می خواهید دستکاری کنید، مشخص کنید:

$user = User::factory()
->has(Post::factory()->count(3), 'posts')
->create();

البته، ممکن است دستکاری های حالت را روی مدل های مرتبط انجام دهید. علاوه بر این، اگر تغییر حالت شما نیاز به دسترسی به مدل والد داشته باشد، می‌توانید یک تبدیل حالت مبتنی بر بسته شدن را تصویب کنید:

$user = User::factory()
->has(
Post::factory()
->count(3)
->state(function (array $attributes, User $user) {
return ['user_type' => $user->type];
})
)
->create();

استفاده از روش های جادویی

برای راحتی، می توانید از روش های رابطه کارخانه جادویی لاراول برای ایجاد روابط استفاده کنید. به عنوان مثال، مثال زیر از قرارداد استفاده می کند تا مشخص کند که مدل های مرتبط باید از طریق یک posts روش رابطه در User مدل ایجاد شوند:

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

هنگام استفاده از روش‌های جادویی برای ایجاد روابط کارخانه، می‌توانید آرایه‌ای از ویژگی‌ها را برای نادیده گرفتن مدل‌های مرتبط ارسال کنید:

$user = User::factory()
->hasPosts(3, [
'published' => false,
])
->create();

اگر تغییر حالت شما نیاز به دسترسی به مدل والد داشته باشد، می‌توانید یک تبدیل حالت مبتنی بر بسته شدن ارائه دهید:

$user = User::factory()
->hasPosts(3, function (array $attributes, User $user) {
return ['user_type' => $user->type];
})
->create();

متعلق به روابط است

اکنون که نحوه ایجاد روابط "بسیار" با استفاده از کارخانه ها را بررسی کردیم، اجازه دهید معکوس این رابطه را بررسی کنیم. این for روش ممکن است برای تعریف مدل مادری که مدل‌های ایجاد شده کارخانه به آن تعلق دارند، استفاده شود. به عنوان مثال، ما می توانیم سه App\Models\Post نمونه مدل ایجاد کنیم که متعلق به یک کاربر است:

use App\Models\Post;
use App\Models\User;
 
$posts = Post::factory()
->count(3)
->for(User::factory()->state([
'name' => 'Jessica Archer',
]))
->create();

اگر قبلاً یک نمونه مدل والد دارید که باید با مدل‌هایی که ایجاد می‌کنید مرتبط باشد، می‌توانید نمونه مدل را به متد ارسال کنید for :

$user = User::factory()->create();
 
$posts = Post::factory()
->count(3)
->for($user)
->create();

استفاده از روش های جادویی

برای راحتی، می‌توانید از روش‌های رابطه کارخانه جادویی لاراول برای تعریف روابط «متعلق به» استفاده کنید. به عنوان مثال، مثال زیر از قرارداد استفاده می کند تا تعیین کند که سه پست باید به user رابطه روی Post مدل تعلق داشته باشند:

$posts = Post::factory()
->count(3)
->forUser([
'name' => 'Jessica Archer',
])
->create();

بسیاری از روابط

مانند بسیاری از روابط ، روابط "بسیاری از بسیاری" ممکن است با استفاده از has روش ایجاد شوند:

use App\Models\Role;
use App\Models\User;
 
$user = User::factory()
->has(Role::factory()->count(3))
->create();

ویژگی های جدول محوری

اگر نیاز به تعریف ویژگی هایی دارید که باید در جدول محوری / میانی که مدل ها را به هم پیوند می دهد تنظیم شود، می توانید از hasAttached روش استفاده کنید. این متد آرایه ای از نام ها و مقادیر ویژگی های جدول محوری را به عنوان آرگومان دوم خود می پذیرد:

use App\Models\Role;
use App\Models\User;
 
$user = User::factory()
->hasAttached(
Role::factory()->count(3),
['active' => true]
)
->create();

اگر تغییر حالت شما نیاز به دسترسی به مدل مرتبط داشته باشد، می‌توانید یک تبدیل حالت مبتنی بر بسته شدن ارائه دهید:

$user = User::factory()
->hasAttached(
Role::factory()
->count(3)
->state(function (array $attributes, User $user) {
return ['name' => $user->name.' Role'];
}),
['active' => true]
)
->create();

اگر قبلاً نمونه‌های مدلی دارید که می‌خواهید به مدل‌هایی که ایجاد می‌کنید متصل شوند، می‌توانید نمونه‌های مدل را به متد ارسال کنید hasAttached . در این مثال، همان سه نقش به هر سه کاربر پیوست می‌شود:

$roles = Role::factory()->count(3)->create();
 
$user = User::factory()
->count(3)
->hasAttached($roles, ['active' => true])
->create();

استفاده از روش های جادویی

برای راحتی، می‌توانید از روش‌های رابطه کارخانه جادویی لاراول برای تعریف بسیاری از روابط استفاده کنید. به عنوان مثال، مثال زیر از قرارداد استفاده می کند تا مشخص کند که مدل های مرتبط باید از طریق یک roles روش رابطه در User مدل ایجاد شوند:

$user = User::factory()
->hasRoles(1, [
'name' => 'Editor'
])
->create();

روابط چند شکلی

روابط چند شکلی نیز ممکن است با استفاده از کارخانه ها ایجاد شود. روابط چند شکلی "morph many" به همان روشی ایجاد می شوند که روابط معمولی "دارای بسیاری" هستند. به عنوان مثال، اگر یک App\Models\Post مدل morphMany با یک App\Models\Comment مدل رابطه داشته باشد:

use App\Models\Post;
 
$post = Post::factory()->hasComments(3)->create();

شکل به روابط

نمی توان از روش های جادویی برای ایجاد morphTo روابط استفاده کرد. در عوض، for روش باید به طور مستقیم استفاده شود و نام رابطه باید به صراحت ارائه شود. به عنوان مثال، تصور کنید که Comment مدل دارای commentable روشی است که یک morphTo رابطه را تعریف می کند. for در این شرایط، می‌توانیم با استفاده از روش مستقیم، سه نظر ایجاد کنیم که متعلق به یک پست است :

$comments = Comment::factory()->count(3)->for(
Post::factory(), 'commentable'
)->create();

چند شکلی از بسیاری به بسیاری از روابط

روابط چند شکلی «بسیاری به بسیاری» ( morphToMany / morphedByMany ) ممکن است درست مانند روابط غیرچند شکلی «بسیاری از بسیاری» ایجاد شوند:

use App\Models\Tag;
use App\Models\Video;
 
$videos = Video::factory()
->hasAttached(
Tag::factory()->count(3),
['public' => true]
)
->create();

البته، has روش جادویی نیز ممکن است برای ایجاد روابط چند شکلی "بسیاری به بسیاری" استفاده شود:

$videos = Video::factory()
->hasTags(3, ['public' => true])
->create();

تعریف روابط درون کارخانه ها

برای تعریف رابطه در کارخانه مدل خود، معمولاً یک نمونه کارخانه جدید را به کلید خارجی رابطه اختصاص می دهید. این معمولاً برای روابط "معکوس" مانند belongsTo و morphTo روابط انجام می شود. به عنوان مثال، اگر می خواهید هنگام ایجاد یک پست یک کاربر جدید ایجاد کنید، می توانید موارد زیر را انجام دهید:

use App\Models\User;
 
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'user_id' => User::factory(),
'title' => $this->faker->title(),
'content' => $this->faker->paragraph(),
];
}

اگر ستون‌های رابطه به کارخانه‌ای بستگی دارد که آن را تعریف می‌کند، می‌توانید یک بسته را به یک ویژگی اختصاص دهید. بسته شدن آرایه ویژگی ارزیابی شده کارخانه را دریافت می کند:

/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'user_id' => User::factory(),
'user_type' => function (array $attributes) {
return User::find($attributes['user_id'])->type;
},
'title' => $this->faker->title(),
'content' => $this->faker->paragraph(),
];
}

در حال اجرا بذرها

اگر می‌خواهید از کاشت‌کننده‌های پایگاه داده برای پر کردن پایگاه داده خود در طول آزمایش ویژگی استفاده کنید، می‌توانید این seed روش را فراخوانی کنید. به‌طور پیش‌فرض، این seed متد، را اجرا می‌کند DatabaseSeeder ، که باید همه بذرهای دیگر شما را اجرا کند. از طرف دیگر، شما یک نام کلاس seeder خاص را به seed متد ارسال می کنید:

<?php
 
namespace Tests\Feature;
 
use Database\Seeders\OrderStatusSeeder;
use Database\Seeders\TransactionStatusSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Tests\TestCase;
 
class ExampleTest extends TestCase
{
use RefreshDatabase;
 
/**
* Test creating a new order.
*
* @return void
*/
public function test_orders_can_be_created()
{
// Run the DatabaseSeeder...
$this->seed();
 
// Run a specific seeder...
$this->seed(OrderStatusSeeder::class);
 
// ...
 
// Run an array of specific seeders...
$this->seed([
OrderStatusSeeder::class,
TransactionStatusSeeder::class,
// ...
]);
}
}

از طرف دیگر، می‌توانید به لاراول دستور دهید قبل از هر آزمایشی که از این RefreshDatabase ویژگی استفاده می‌کند، پایگاه داده را به‌طور خودکار بذر کند. شما می توانید این کار را با تعریف یک $seed ویژگی در کلاس تست پایه خود انجام دهید:

<?php
 
namespace Tests;
 
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
 
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
 
/**
* Indicates whether the default seeder should run before each test.
*
* @var bool
*/
protected $seed = true;
}

زمانی که $seed ویژگی باشد true ، تست Database\Seeders\DatabaseSeeder قبل از هر تستی که از این RefreshDatabase صفت استفاده می کند، کلاس را اجرا می کند. با این حال، می توانید یک seder خاص را مشخص کنید که باید با تعریف یک $seeder ویژگی در کلاس آزمایشی خود اجرا شود:

use Database\Seeders\OrderStatusSeeder;
 
/**
* Run a specific seeder before each test.
*
* @var string
*/
protected $seeder = OrderStatusSeeder::class;

ادعاهای موجود

لاراول چندین ادعای پایگاه داده را برای تست های ویژگی PHPUnit شما ارائه می دهد . در زیر به هر یک از این ادعاها خواهیم پرداخت.

assertDatabaseCount

ادعا کنید که یک جدول در پایگاه داده حاوی تعداد داده شده رکورد است:

$this->assertDatabaseCount('users', 5);

assertDatabaseHas

ادعا کنید که یک جدول در پایگاه داده حاوی رکوردهایی است که با قیود کلید/مقدار داده شده مطابقت دارند:

$this->assertDatabaseHas('users', [
'email' => 'sally@example.com',
]);

assertDatabase Missing

ادعا کنید که یک جدول در پایگاه داده حاوی رکوردهای منطبق با محدودیت های پرس و جو کلید / مقدار نیست:

$this->assertDatabaseMissing('users', [
'email' => 'sally@example.com',
]);

ادعا حذف شد

ادعا assertDeleted می کند که یک مدل Eloquent داده شده از پایگاه داده حذف شده است:

use App\Models\User;
 
$user = User::find(1);
 
$user->delete();
 
$this->assertDeleted($user);

این assertSoftDeleted روش ممکن است برای ادعای اینکه مدل Eloquent داده شده "نرم حذف شده" است استفاده شود:

$this->assertSoftDeleted($user);

assertModelExists

ادعا کنید که یک مدل داده شده در پایگاه داده وجود دارد:

use App\Models\User;
 
$user = User::factory()->create();
 
$this->assertModelExists($user);

assertModel Missing

ادعا کنید که یک مدل داده شده در پایگاه داده وجود ندارد:

use App\Models\User;
 
$user = User::factory()->create();
 
$user->delete();
 
$this->assertModelMissing($user);