codenco-dev/eloquent-model-tester

简化您的 Laravel 项目中 Eloquent 模型的测试

v3.0.0 2024-05-05 14:06 UTC

This package is auto-updated.

Last update: 2024-09-05 14:45:59 UTC


README

Latest Version on Packagist StyleCI Build Status Quality Score Total Downloads

此包允许您测试您的模型关于表结构、关系等

安装

您可以通过 composer 安装此包

composer require codenco-dev/eloquent-model-tester --dev

用法

要使用此包,您必须为您模型生成工厂。(见 工厂文档)您可以为单个模型或多个模型生成一个测试文件。例如,对于您的模型 MyModel,您可以使用以下命令

php artisan make:model MyModel -mf
php artisan make:test Models/MyModelTest

为了能够测试数据库,别忘了使用 RefreshDatabase 语句。

namespace Tests\Feature\Models;

use App\MyModel;
use CodencoDev\EloquentModelTester\HasModelTester;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class MyModelTest extends TestCase
{
    use RefreshDatabase;
    use HasModelTester;

    public function test_have_my_model_model()
    {
        //...
    }
}

为了更简单,您可以将 RefreshDatabase 使用语句放入 tests/TestCase.php 文件

结构和填充/保护测试

有这个结构

users
    id - integer
    name - string
    email - string
    password - string
    remember_token - string
    other_field - string
    created_at - timestamp
    updated_at - timestamp

您可以测试是否拥有所有需要的字段以及它们是否可填充或受保护。

class UserTest extends TestCase
{
    use HasModelTestor;

    public function test_have_user_model()
    {
        $this->modelTestable(User::class)
            ->assertHasColumns(['id','name','email','password','remember_token'])
            ->assertHasColumnsInFillable(['name','password'])
            ->assertHasColumnsInGuarded(['remember_token'])
            ->assertHasTimestampsColumns();
    }
}

assertHasColumns() 函数仅检查提供的值是否在表的架构中。如果您想确保没有其他列存在,您可以使用更严格的 assertHasOnlyColumns() 断言来检查只存在提供的列名。

注意。当使用此功能时,如果您数据库表中存在 created_atupdated_at 字段,您需要提供这些字段。

assertHasColumnsInFillable()assertHasColumnsInGuarded() 函数仅检查 $fillable$guarded 数组中是否包含传递的值。如果您想确保 $fillable$guarded 数组中没有其他条目,则可以使用更严格的 assertHasOnlyColumnsInFillable()assetHasOnlyColumnsInGuarded() 函数,如下所示

class UserTest extends TestCase
{
    use HasModelTestor;

    public function test_have_user_model()
    {
        $this->modelTestable(User::class)
            ->assertHasOnlyColumns(['id','name','email','password','remember_token', 'created_at', 'updated_at']) // Will fail as missing 'other_field'.
            ->assertHasOnlyColumnsInFillable(['name','password'])
            ->assertHasOnlyColumnsInGuarded(['remember_token']);
    }
}

为了进一步确认只有一组列可以填充,您可以使用 assertCanOnlyFill() 断言来确认。此断言通过确认这些字段是 $fillable 数组中的唯一值,并且它们不出现在 $guarded 数组中来确认这一点。

class User extends Model
{
    $fillable = ['name', 'password', 'email'];
    
    $guarded = ['remember_token'];
}

class UserTest extends TestCase
{
    use HasModelTestor;

    public function test_have_user_model()
    {
        $this->modelTestable(User::class)
            ->assertHasColumns(['id','name','email','password','remember_token'])
            ->assertCanOnlyFill(['name','password']); // Will fail as 'email' is in the fillable array.
    }
}

为了确认一个字段由于错误而同时出现在 $fillable$guarded 数组中,有 assertNoGuardedAndFillableFields() 断言来检查没有条目同时出现在两者中

class User extends Model
{
    $fillable = ['name', 'password', 'email'];
    
    $guarded = ['email'];
}

class UserTest extends TestCase
{
    use HasModelTestor;

    public function test_have_user_model()
    {
        $this->modelTestable(User::class)
            ->assertNoGuardedAndFillableFields(); // Will fail as 'email' is in both the fillable & guarded arrays.
    }
}

要检查模型上的软删除 deleted_at 列,可以使用 assertHasSoftDeleteTimestampColumns() 断言

user:
    id - integer
    name - string
    deleted_at - timestamp
use Illuminate\Database\Eloquent\SoftDeletes;

class User extends Model
{
    use SoftDeletes;
}

class UserTest extends TestCase
{
    use HasModelTestor;

    public function test_have_user_model()
    {
        $this->modelTestable(User::class)
            ->assertHasSoftDeleteTimestampColumns();
    }
}

HasOne 和 BelongsTo

您可以测试模型的关系。例如,有这个结构

user
    id - integer
    name - string

phones
    id - integer
    number - string
    user_id - integer

您可以使用 assertHasHasOneRelation()assertHasBelongsToRelations 方法如下

class UserTest extends TestCase
{
    use HasModelTestor;

    public function test_have_category_model()
    {
        $this->modelTestable(User::class)
            ->assertHasHasOneRelation(Phone::class);
    }

}

class PhoneTest extends TestCase
{
    use HasModelTestor;

    public function test_have_customer_model()
    {
        $this->modelTestable(Phone::class)
            ->assertHasBelongsToRelation(User::class);
    }
}

HasMany 和 BelongsTo

您可以测试模型的关系。例如,有这个结构

categories
    id - integer
    name - string

customers
    id - integer
    name - string
    category_id - integer
    type_id - integer

您可以使用 assertHasHasManyRelationsassertHasBelongsToRelations 方法如下

class CategoryTest extends TestCase
{
    use HasModelTestor;

    public function test_have_category_model()
    {
        $this->modelTestable(Category::class)
            ->assertHasHasManyRelation(Customer::class);
    }

}

class CustomerTest extends TestCase
{
    use HasModelTestor;

    public function test_have_customer_model()
    {
        $this->modelTestable(Customer::class)
            ->assertHasBelongsToRelation(Category::class);
    }
}

如果您不使用 Laravel 命名约定,您也可以通过向 assertHasHasManyRelationsassertHasBelongsToRelations 方法传递额外的参数来覆盖关系和本地键(对于 belongsTo 关系)

    $this->modelTestable(Customer::class)
            ->assertHasBelongsToRelation(Category::class,'category','category_id');

    $this->modelTestable(Category::class)
            ->assertHasHasManyRelation(Customer::class,'customers');

如果您有多个关系,您可以使用如下方式链式调用方法

    $this->modelTestable(Customer::class)
            ->assertHasBelongsToRelation(Category::class)
            ->assertHasBelongsToRelation(OtherModel::class);

HasManyThrough 关系

您可以测试模型上 has many through 关系。例如,有这个结构

customers
    id - integer
    name - string

locations
    id - integer
    customer_id - integer

orders
    id - integer
    location_id - integer

您可以使用 assertHasHasManyThroughRelations 方法如下

class CustomersTest extends TestCase
{
    use HasModelTestor;

    public function test_have_orders_model()
    {
        $this->modelTestable(Customer::class)
            ->assertHasHasManyThroughRelation(Order::class, Location::class);
    }

}

如果您不使用 Laravel 命名约定,您也可以通过向 assertHasHasManyThroughRelations 方法传递额外的参数来覆盖关系和外部键

    $this->modelTestable(Customer::class)
            ->assertHasHasManyThroughRelation(Order::class, Location::class, 'sales', 'prefix_customer_id', 'prefix_location_id', 'firstPrimaryKey', 'secondPrimaryKey');

注意:由于这个关系没有官方的逆关系,因此不能在反向使用这个断言,即在 orders 模型检查 customers 关系时。

多对多关系

您可以使用 ManyToManyRelationsTestable 特性来测试您的多对多关系。

users
    id - integer
    name - string

roles
    id - integer
    name - string

role_user
    user_id - integer
    role_id - integer
class UserTest extends TestCase
{
     use HasModelTestor;

    public function test_have_user_model()
    {
        $this->modelTestable(User::class)
            ->assertHasManyToManyRelation(Role::class);
    }


}

class RoleTest extends TestCase
{
    use HasModelTestor;

    public function test_have_role_model()
    {
        $this->modelTestable(User::class)
            ->assertHasManyToManyRelation(User::class);
    }

}

您也可以重写关系参数。

    $this->modelTestable(User::class)
            ->assertHasManyToManyRelation(User::class,'users');

形态关系

如果您有一个形态关系,

posts
    id - integer
    title - string
    body - text

videos
    id - integer
    title - string
    url - string

comments
    id - integer
    body - text
    commentable_id - integer
    commentable_type - string

您可以使用如下方式使用 assertHasBelongsToMorphRelationsassertHasHasManyMorphRelations 方法

class PostTest extends TestCase
{

    use HasModelTestor;

    public function test_have_post_model()
        {
            $this->modelTestable(Post::class)
                ->assertHasHasManyMorphRelation(Comment::class,'comments');
        }
}

class VideoTest extends TestCase
{
    use HasModelTestor;

    public function test_have_video_model()
        {
            $this->modelTestable(Video::class)
                ->assertHasHasManyMorphRelation(Comment::class,'comments');
        }
}

class CommentTest extends TestCase
{

    use HasModelTestor;

    public function test_have_morph_model_model()
    {
        $this->modelTestable(Comment::class)
           ->assertHasBelongsToMorphRelation(Post::class,'commentable')
           ->assertHasBelongsToMorphRelation(Video::class,'commentable');
    }
}

形态一对一关系

如果您有一个形态一对一关系,

posts
    id - integer
    title - string
    body - text

users
    id - integer
    title - string

images
    id - integer
    url - string
    imageable_id - integer
    imageable_type - string

您可以使用如下方式使用 assertHasBelongsToMorphRelationsassertHasMorphOneRelations 方法

class PostTest extends TestCase
{

    use HasModelTestor;

    public function test_have_post_model()
        {
            $this->modelTestable(Post::class)
                ->assertHasMorphOneRelation(Image::class);
        }
}

class UserTest extends TestCase
{
    use HasModelTestor;

    public function test_have_user_model()
        {
            $this->modelTestable(User::class)
                ->assertHasMorphOneRelation(Image::class, 'avatar');
        }
}

class ImageTest extends TestCase
{

    use HasModelTestor;

    public function test_have_image_model()
    {
        $this->modelTestable(Image::class)
           ->assertHasBelongsToMorphRelation(Post::class,'imageable')
           ->assertHasBelongsToMorphRelation(User::class,'imageable');
    }
}

没有模型的主表和从表

您可以使用 tableTestable 方法测试一个表是否包含具有的列。

class MyPivotTest extends TestCase
{
    public function test_have_table_without_model()
    {
        $this->tableTestable('pivot_table')
            ->assertHasColumns(['first_model_id','second_model_id','other_property']);
    }
}

模型作用域

您可以使用 assertHasScope() 断言来测试一个模型是否定义了查询作用域。

class User extends Model
{
    /**
     * Scope a query to only include popular users.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopePopular($query)
    {
        return $query->where('votes', '>', 100);
    }
}

class UserTest extends TestCase
{
    use HasModelTestor;

    public function test_have_user_model()
    {
        $this->modelTestable(User::class)
            ->assertHasScope('popular');
    }
}

当然,如果您使用动态作用域,则 assertHasScope() 接收传递给查询作用域的参数。

class User extends Model
{
    /**
    * Scope a query to only include users of a given type.
    *
    * @param  \Illuminate\Database\Eloquent\Builder  $query
    * @param  mixed  $type
    * @return \Illuminate\Database\Eloquent\Builder
    */
    public function scopeOfType($query, $type)
    {
        return $query->where('type', $type);
    }
}

class UserTest extends TestCase
{
    use HasModelTestor;

    public function test_have_user_model()
    {
        $this->modelTestable(User::class)
            ->assertHasScope('ofType', 'admin');
    }
}

可用的断言

测试

composer test

变更日志

有关最近更改的更多信息,请参阅 变更日志

贡献

有关详细信息,请参阅 贡献指南

安全性

如果您发现任何安全问题,请通过电子邮件 dthomas@codenco.fr 或在 Twitter 上联系我 DomThomasEs,而不是使用问题跟踪器。

鸣谢

许可

MIT 许可证 (MIT)。有关更多信息,请参阅 许可文件

Laravel 包模板

此包是使用 Laravel 包模板 生成的。