Introduction
Let's imagine that we are developing a small web application on Laravel version higher than 6 and we want to write tests for it.
The content of the article is given below:
- Domain Description
- Application creation
- Entity Creation
- Writing tests
- Problem
- Decision
Domain Description
We will develop an online store in which certain users can make a certain order. From the above we get that the main entities of the subject area will be the user, order and goods. There is a one-to-many relationship between the user and the order, that is, the user can have many orders, and the order has only one user (a user is required for the order). There is a many-to-many relationship between the order and the goods, because the goods can be in different orders and the order can consist of many goods. For simplicity, we omit products and focus only on users and orders.
Application creation
Laravel applications are very easy to build using the application maker package . After installing it, creating a new application fits into one command:
laravel new shop
Entity Creation
, — . Laravel , . , . :
php artisan make:model Order -m -f
App/, database/migrations/ database/factories/.
. , . - :
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateOrdersTable extends Migration
{
public function up()
{
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')
->onDelete('cascade');
});
}
public function down()
{
Schema::dropIfExists('orders');
}
}
. fillable :
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Order extends Model
{
protected $fillable = ['user_id'];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
. , , id.
<?php
use App\Order;
use App\User;
use Faker\Generator as Faker;
$factory->define(Order::class, function (Faker $faker) {
return [
'user_id' => factory(User::class)->create()->id
];
});
, .
, Laravel PHPUnit . :
php artisan make:test OrderTest
tests/Feature/. RefreshDatabase.
№1.
<?php
namespace Tests\Feature;
use App\Order;
use App\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class OrderTest extends TestCase
{
use RefreshDatabase;
public function order_factory_can_create_order()
{
$order = factory(Order::class)->create();
$this->assertInstanceOf(Order::class, $order);
}
}
!
№2.
public function order_should_have_user_relation()
{
$order = factory(Order::class)->create();
$this->assertNotEmpty($order->user_id);
$this->assertInstanceOf(User::class, $order->user);
}
!
№3. ,
public function we_can_provide_user_id_to_order_factory()
{
$user = factory(User::class)->create();
$order = factory(Order::class)->create(['user_id' => $user->id]);
$this->assertEquals($user->id, $order->user_id);
}
!
, , . , , .
№4. ,
public function when_we_create_one_order_one_user_should_be_created()
{
$user = factory(User::class)->create();
$order = factory(Order::class)->create(['user_id' => $user->id]);
$this->assertEquals($user->id, $order->user_id);
$this->assertEquals(1, User::count());
}
! , . ? .
, , — . , , . . , , . , .
, . PHP , n- — func_get_arg(), . , () Faker, — , create() . , , () . , , . :
$factory->define(Order::class, function (Faker $faker) {
$passedArguments = func_get_arg(1);
return [
'user_id' => function () use ($passedArguments) {
if (! array_key_exists('user_id', $passedArguments)) {
return factory(User::class)->create()->id;
}
}
];
});
№4 — !
That is all I wanted to share. I often encounter the problem that it is necessary to control the number of entities after some action inside the system, and the standard factory implementation does not really cope with this.
I will be glad to hear your tricks that you use when developing in Laravel or PHP.