Truques para testar aplicativos Web Laravel usando fábricas de modelos

Introdução


Vamos imaginar que estamos desenvolvendo uma pequena aplicação Web na versão Laravel superior a 6 e queremos escrever testes para ela.


O conteúdo do artigo é fornecido abaixo:


  1. Descrição de Domínio
  2. Criação de aplicativo
  3. Criação de Entidades
  4. Testes de escrita
  5. Problema
  6. Decisão

Descrição de Domínio


Vamos desenvolver uma loja online na qual determinados usuários podem fazer um determinado pedido. Pelo exposto, concluímos que as principais entidades da área de assunto serão o usuário, o pedido e as mercadorias. Há uma comunicação de um para muitos entre o usuário e o pedido, ou seja, o usuário pode ter muitos pedidos e o pedido possui apenas um usuário (para o pedido, é necessária a presença do usuário). Há um relacionamento muitos-para-muitos entre o pedido e os bens, porque os bens podem estar em pedidos diferentes e o pedido pode consistir em muitos bens. Por simplicidade, omitimos produtos e focamos apenas em usuários e pedidos.


Criação de aplicativo


Os aplicativos Laravel são muito fáceis de construir usando o pacote do criador de aplicativos . Após a instalação, a criação de um novo aplicativo se encaixa em um comando:


laravel new shop

Criação de Entidades


, — . 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 — !


Isso é tudo que eu queria compartilhar. Costumo encontrar o problema de que é necessário controlar o número de entidades após alguma ação dentro do sistema, e a implementação padrão da fábrica realmente não lida com isso.


Ficarei feliz em ouvir seus truques que você usa ao desenvolver no Laravel ou PHP.


All Articles