测试中数据播种简介

发布于 作者:

Introduction to Seeding Data in Testing image

自从 Laravel 5.1 中发布了数据播种功能以来,测试变得更加容易和快捷。

您可以在测试开始之前插入十个用户,每个用户都有一个帖子,或者插入 1000 个用户,每个用户都有一个或多个帖子。

在本教程中,您将创建一个测试用例来测试用户模型,并创建一个播种器来将十个用户播种到数据库中,每个用户都关注一个用户。

首先,我们需要创建数据库表。

迁移

创建一个表来存储用户之间的关系(谁关注谁)。

# database/migrations/2014_10_12_000000_create_users_table.php
class CreateUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
 
// following table is storing the relationship between users
// user_id is following follow_user_id
Schema::create('following', function (Blueprint $table) {
$table->integer('user_id')->unsigned()->index();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
 
$table->integer('follow_user_id')->unsigned()->index();
$table->foreign('follow_user_id')->references('id')->on('users')->onDelete('cascade');
 
$table->timestamps();
});
}
 
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('following');
Schema::dropIfExists('users');
}
}

接下来,运行迁移。

php artisan migrate
Migration table created successfully.

如果您的应用程序版本是 5.4,并且您看到了以下错误

[Illuminate\Database\QueryException]
 
SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes (SQL: alter table `users` add unique `users_email_unique`(`email`))
[PDOException]
SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes

不要惊慌!查看这篇 文章 了解解决方法。

在应用了解决方法后,删除之前创建的表,然后再次运行迁移。

php artisan migrate
Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table

数据库已准备就绪。现在,让我们准备用户模型。

用户模型

用户模型是本例中的测试对象,它有一些方法来创建和检索用户之间的关系。

# app/User.php
class User extends Authenticatable
{
use Notifiable;
 
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'email', 'password',
];
 
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token',
];
 
public function follows(User $user)
{
$this->following()->attach($user->id);
}
 
public function unfollows(User $user)
{
$this->following()->detach($user->id);
}
 
public function following()
{
return $this->belongsToMany('App\User', 'following', 'user_id', 'follow_user_id')->withTimestamps();
}
 
public function isFollowing(User $user)
{
return !is_null($this->following()->where('follow_user_id', $user->id)->first());
}
}

用户模型已准备好进行测试。接下来,我们将数据插入数据库。

数据播种

Laravel 使执行数据播种变得非常容易。播种器类默认包含一个 run 方法。您可以使用查询构建器或 Eloquent 模型工厂来插入数据。

让我们运行一个 Artisan 命令来生成一个播种器。

php artisan make:seeder UsersTableSeeder

然后,您使用 模型工厂 在 run 方法中生成十个用户。

# database/seeds/UsersTableSeeder.php
use App\User;
use Illuminate\Database\Seeder;
 
class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$users = factory(User::class, 10)->create();
}
}

运行 Artisan 命令来执行数据播种。

php artisan db:seed --class=UsersTableSeeder

您也可以在 DatabaseSeeder 的 run 方法中启用对 UsersTableSeeder 的调用。

# database/seeds/UsersTableSeeder.php
use App\User;
use Illuminate\Database\Seeder;
 
class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$users = factory(User::class, 10)->create();
}
}

运行 Artisan 命令来执行数据播种。

php artisan db:seed --class=UsersTableSeeder

您也可以在 DatabaseSeeder 的 run 方法中启用对 UsersTableSeeder 的调用。

# database/seeds/DatabaseSeeder.php
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$this->call(UsersTableSeeder::class);
}
}

接下来,运行命令而不声明播种器类名称。

php artisan db:seed

使用 DatabaseSeeder,您可以执行多个播种器类。

现在,我们将着手处理我们的测试用例。

测试用例

在本教程中,您的测试用例的目标是测试用户模型中的 follow 和 unfollow 方法。

我们将从生成一个新的测试开始。

php artisan make:test UserTest

先决条件测试

首先,我们将创建一个测试以确保数据库中有十个用户。

# tests/Feature/UserTest.php
use App\User;
 
class UserTest extends TestCase
{
public function test_have_10_users()
{
$this->assertEquals(10, User::count());
}
}

运行 PHPUnit。

phpunit

如果它不起作用,请尝试以下操作。

vendor/bin/phpunit
PHPUnit 5.7.17 by Sebastian Bergmann and contributors.
... 3 / 3 (100%)
Time: 2.72 seconds, Memory: 12.00MB
OK (3 tests, 3 assertions)

为什么 PHPUnit 不起作用?也许您的机器版本不符合 Laravel 的要求。Laravel 通过 Composer 预安装了 PHPUnit。您可以运行它。在我的情况下,vendor/bin/phpunit 工作正常。在接下来的教程中,我将使用 vendor/bin/phpunit。

测试

通过完成上一步,您确认数据播种和单元测试正常工作。它是创建真实测试的绿灯。

# tests/Feature/UserTest.php
public function test_follows()
{
$userA = User::find(2);
$userB = User::find(3);
 
$userA->follows($userB);
 
$this->assertEquals(2, $userA->following()->count());
}
 
public function test_unfollows()
{
$userA = User::find(3);
$userB = User::find(2);
 
$userA->unfollows($userB);
 
$this->assertEquals(0, $userA->following()->count());
}
 
public function test_A_follows_B_and_C()
{
$userA = User::find(1);
 
$ids = collect([2, 3, 4, 5, 6, 7, 8, 9, 10]);
$random_ids = $ids->random(2);
 
$userB = User::find($random_ids->pop());
$userC = User::find($random_ids->pop());
 
$userA->follows($userB);
$userA->follows($userC);
 
$this->assertEquals(2, $userA->following()->count());
}

再次运行 PHPUnit。

vendor\bin\phpunit
PHPUnit 5.7.17 by Sebastian Bergmann and contributors.
...... 6 / 6 (100%)
Time: 1.23 seconds, Memory: 10.00MB
OK (6 tests, 6 assertions)

所有测试都通过了!酷!我建议您再次运行测试以测试一致性。

vendor\bin\phpunit
PHPUnit 5.7.17 by Sebastian Bergmann and contributors.
..F.F. 6 / 6 (100%)
Time: 1.25 seconds, Memory: 10.00MB
There were 2 failures:
1) Tests\Feature\UserTest::test_follows
Failed asserting that 3 matches expected 2.
C:\xampp\htdocs\TestWithSeed\tests\Feature\UserTest.php:26
2) Tests\Feature\UserTest::test_A_follows_B_and_C
Failed asserting that 4 matches expected 2.
C:\xampp\htdocs\TestWithSeed\tests\Feature\UserTest.php:52
 
FAILURES!
Tests: 6, Assertions: 6, Failures: 2.

糟糕!两个测试失败了。发生了什么事?

当您第一次运行测试时,测试已经对数据库中的数据进行了更改。因此,当您再次运行测试时,更改后的数据会影响测试结果。

这并不意味着应用程序有错误。测试用例有责任处理这种情况。我建议在每次测试运行之前重置数据库。

重置数据库

建议一,在 PHPUnit 之前运行迁移刷新和数据播种。

php artisan migrate:refresh --seed
vendor\bin\phpunit

更好的建议是使用 Traits。Laravel 提供两种在每次测试后重置数据库的方法。它们是 DatabaseMigrations 和 DatabaseTransactions。

让我们尝试一下 DatabaseMigrations。

# tests/Feature/UserTest.php
class UserTest extends TestCase
{
use DatabaseTransactions;
 
...
}

运行测试。

vendor\bin\phpunit
PHPUnit 5.7.17 by Sebastian Bergmann and contributors.
 
..EEE. 6 / 6 (100%)
 
Time: 5.53 seconds, Memory: 12.00MB
 
There were 3 errors:
 
1) Tests\Feature\UserTest::test_follows
Error: Call to a member function follows() on null
 
C:\xampp\htdocs\seeding-data-in-the-testing\tests\Feature\UserTest.php:25
 
2) Tests\Feature\UserTest::test_unfollows
Error: Call to a member function unfollows() on null
 
C:\xampp\htdocs\seeding-data-in-the-testing\tests\Feature\UserTest.php:35
 
3) Tests\Feature\UserTest::test_A_follows_B_and_C
Error: Call to a member function follows() on null
 
C:\xampp\htdocs\seeding-data-in-the-testing\tests\Feature\UserTest.php:50
 
ERRORS!
Tests: 6, Assertions: 3, Errors: 3.

不好。迁移正在工作,但数据播种没有在 DatabaseMigrations 上调用。

没关系;让我们尝试一下 DatabaseTransactions。

# tests/Feature/UserTest.php
class UserTest extends TestCase
{
use DatabaseTransactions;
 
...
}

再次运行测试。

vendor\bin\phpunit
PHPUnit 5.7.17 by Sebastian Bergmann and contributors.
 
...... 6 / 6 (100%)
 
Time: 7.29 seconds, Memory: 14.00MB
 
OK (6 tests, 6 assertions)

一切都很好!DatabaseTransactions Trait 将每个测试的查询包装在一个事务中,因此来自先前测试的数据不会干扰后续测试。

结论

使用计划的测试数据播种您的数据库可以模拟许多不同的情况,并毫不费力地加载大量数据。

少即是多。用更少的努力获得更好的结果。

您可以使用来自 这里 的示例项目。

Sky Chin photo

Sky 是 Medium 上“使用 Laravel 重建 Twitter”一文的作者,也是 iteachyouhowtocode.com 的所有者

Cube

Laravel 新闻通讯

加入 40,000 多名其他开发人员,绝不错过新的技巧、教程等等。

Laravel Forge logo

Laravel Forge

轻松创建和管理您的服务器,并在几秒钟内部署您的 Laravel 应用程序。

Laravel Forge
Tinkerwell logo

Tinkerwell

Laravel 开发人员必备的代码运行器。使用 AI、自动补全和本地和生产环境的即时反馈进行调试。

Tinkerwell
No Compromises logo

绝不妥协

Joel 和 Aaron,来自“绝不妥协”播客的两位经验丰富的开发人员,现在可以为您的 Laravel 项目提供雇佣服务。 ⬧ 固定价格 7500 美元/月。 ⬧ 没有冗长的销售流程。 ⬧ 没有合同。 ⬧ 100% 退款保证。

绝不妥协
Kirschbaum logo

Kirschbaum

提供创新和稳定性,以确保您的 Web 应用程序取得成功。

Kirschbaum
Shift logo

Shift

正在运行旧版本的 Laravel?即时、自动化的 Laravel 升级和代码现代化,使您的应用程序保持新鲜。

Shift
Bacancy logo

Bacancy

让您的项目充满活力,聘请经验丰富、拥有 4-6 年经验的 Laravel 开发人员,每月只需 2500 美元。获得 160 小时的专业知识和 15 天无风险试用。立即安排电话!

Bacancy
Lucky Media logo

Lucky Media

现在就变得幸运 - Laravel 开发的理想选择,拥有十多年的经验!

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar:Laravel 电子商务

Laravel 电子商务。一个开源包,将现代无头电子商务功能的强大功能带入 Laravel。

Lunar:Laravel 电子商务
LaraJobs logo

LaraJobs

官方 Laravel 工作板

LaraJobs
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit:Laravel SaaS 启动工具包

SaaSykit 是一个 Laravel SaaS 启动工具包,包含运行现代 SaaS 所需的所有功能。支付、精美结账、管理面板、用户仪表板、身份验证、即用型组件、统计、博客、文档等等。

SaaSykit:Laravel SaaS 启动工具包
Rector logo

Rector

您的无缝 Laravel 升级合作伙伴,降低成本,加速创新,助您企业成功

Rector
MongoDB logo

MongoDB

通过 MongoDB 和 Laravel 的强大集成增强您的 PHP 应用程序,使开发人员能够轻松高效地构建应用程序。支持事务、搜索、分析和移动用例,同时使用熟悉的 Eloquent API。了解灵活、现代的 MongoDB 数据库如何改变您的 Laravel 应用程序。

MongoDB
Maska is a Simple Zero-dependency Input Mask Library image

Maska 是一个简单的零依赖输入掩码库

阅读文章
Add Swagger UI to Your Laravel Application image

在您的 Laravel 应用程序中添加 Swagger UI

阅读文章
Assert the Exact JSON Structure of a Response in Laravel 11.19 image

在 Laravel 11.19 中断言响应的精确 JSON 结构

阅读文章
Build SSH Apps with PHP and Laravel Prompts image

使用 PHP 和 Laravel Prompts 构建 SSH 应用程序

阅读文章
Building fast, fuzzy site search with Laravel and Typesense image

使用 Laravel 和 Typesense 构建快速、模糊的网站搜索

阅读文章
Add Comments to your Laravel Application with the Commenter Package image

使用 Commenter 包向您的 Laravel 应用程序添加评论

阅读文章