Astuces pour tester les applications Web Laravel Ă  l'aide de fabriques de modĂšles

introduction


Imaginons que nous développons une petite application web sur une version Laravel supérieure à 6 et que nous voulons écrire des tests pour celle-ci.


Le contenu de l'article est donné ci-dessous:


  1. Description du domaine
  2. Création d'application
  3. Création d'entité
  4. Tests d'Ă©criture
  5. ProblĂšme
  6. DĂ©cision

Description du domaine


Nous dĂ©velopperons une boutique en ligne dans laquelle certains utilisateurs pourront passer une certaine commande. De ce qui prĂ©cĂšde, nous obtenons que les principales entitĂ©s du domaine seront l'utilisateur, la commande et les marchandises. Il existe une relation un-Ă -plusieurs entre l'utilisateur et la commande, c'est-Ă -dire que l'utilisateur peut avoir plusieurs commandes et que la commande n'a qu'un seul utilisateur (un utilisateur est requis pour la commande). Il existe une relation plusieurs Ă  plusieurs entre la commande et les marchandises, car les marchandises peuvent ĂȘtre dans des commandes diffĂ©rentes et la commande peut ĂȘtre composĂ©e de plusieurs marchandises. Par souci de simplicitĂ©, nous omettons les produits et nous nous concentrons uniquement sur les utilisateurs et les commandes.


Création d'application


Les applications Laravel sont trÚs faciles à construire à l' aide du package du créateur d'application . AprÚs l'avoir installé, la création d'une nouvelle application tient dans une seule commande:


laravel new shop

Création d'entité


, — . 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
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    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');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    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'];

    /**
     * Relation to user
     * @return BelongsTo
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
}

. , , id.


<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

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;

    /** @test */
    public function order_factory_can_create_order()
    {
        // When we use Order factory
        $order = factory(Order::class)->create();
        // Then we should have new Order::class instance
        $this->assertInstanceOf(Order::class, $order);
    }
}

!


№2.


/** @test */
public function order_should_have_user_relation()
{
    // When we use Order factory
    $order = factory(Order::class)->create();
    // Then we should have new Order::class instance with user relation
    $this->assertNotEmpty($order->user_id);
    $this->assertInstanceOf(User::class, $order->user);
}

!


№3. ,


/** @test */
public function we_can_provide_user_id_to_order_factory()
{
    // Given user
    $user = factory(User::class)->create();
    // When we use Order factory and provide user_id parameter
    $order = factory(Order::class)->create(['user_id' => $user->id]);
    // Then we should have new Order::class instance with provided user_id
    $this->assertEquals($user->id, $order->user_id);
}

!


, , . , , .


№4. ,


/** @test */
public function when_we_create_one_order_one_user_should_be_created()
{
    // Given user
    $user = factory(User::class)->create();
    // When we use Order factory and provide user_id parameter
    $order = factory(Order::class)->create(['user_id' => $user->id]);
    // Then we should have new Order::class instance with provided user_id
    $this->assertEquals($user->id, $order->user_id);
    // Let's check that system has one user in DB
    $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) {
            //    user_id,   
            if (! array_key_exists('user_id', $passedArguments)) {
                return factory(User::class)->create()->id;
            }
        }
    ];
});

№4 — !


C'est tout ce que je voulais partager. Je rencontre souvent le problÚme qu'il est nécessaire de contrÎler le nombre d'entités aprÚs une action à l'intérieur du systÚme, et l'implémentation d'usine standard ne résout pas vraiment cela.


Je serai heureux d'entendre vos astuces que vous utilisez lors du développement en Laravel ou PHP.


All Articles