laracasts/testdummy

简易测试存根

2.5 2020-09-08 18:41 UTC

README

TestDummy 使为集成测试准备工厂(模拟数据)的过程尽可能简单。就像...

使用模拟属性构建一个 Post 模型。

use Laracasts\TestDummy\Factory;

$post = Factory::build('Post');

如果我们执行 $post->toArray(),可能会返回

array(4) {
  ["title"]=>
  string(21) "The Title of the Post"
  ["author_id"]=>
  string(1) "5"
  ["body"]=>
  string(226) "Iusto qui optio et iste. Cumque aliquid et omnis enim. Nesciunt ad esse a reiciendis expedita quidem veritatis. Nostrum repellendus reiciendis distinctio amet sapiente. Eum molestias a recusandae modi aut et adipisci corrupti."
  ["publish_date"]=>
  string(19) "2014-03-02 11:05:48"
}

构建一个帖子,但覆盖默认标题。

use Laracasts\TestDummy\Factory;

$post = Factory::build('Post', ['title' => 'Override Title']);

再次,当转换为数组...

array(4) {
  ["title"]=>
  string(14) "Override Title"
  ["author_id"]=>
  string(1) "5"
  ["body"]=>
  string(254) "In eos porro qui est rerum possimus voluptatem non. Repudiandae eaque nostrum eaque aut deleniti possimus quod minus. Molestiae commodi odit sunt dignissimos corrupti repudiandae quibusdam quo. Autem maxime tenetur autem corporis aut quis sint occaecati."
  ["publish_date"]=>
  string(19) "2013-06-24 10:01:30"
}

构建模型的属性数组。

$post = Factory::attributesFor('Post');

build()attributesFor() 的区别在于,前者将返回给定模型类型(如 Post)的实例。后者将简单地返回生成的属性数组,这在某些情况下可能很有用。

构建并持久化一个歌曲实体。

use Laracasts\TestDummy\Factory;

$song = Factory::create('Song');

创建并持久化三次评论。

use Laracasts\TestDummy\Factory;

Factory::times(3)->create('Comment');

实际上,这将使您的 comments 表中具有三行。如果该表有关系(如所有者 Post),则相关行也将使用模拟数据创建。

用法

第1步:安装

通过 Composer 拉取此包,就像任何其他包一样。

"require-dev": {
    "laracasts/testdummy": "~2.0"
}

第2步:创建一个工厂文件

TestDummy 不是魔法。您需要描述应生成的数据类型。

tests/factories 目录中,您可以创建任意数量的 PHP 文件,这些文件将由 TestDummy 自动加载。您为什么不从一个通用的 tests/factories/factories.php 文件开始呢。

您创建的每个工厂文件都将自动访问两个变量

  • $factory
  • $faker

$factory 是您将用来定义新数据集的函数,例如 Post 或专辑的构成。

$factory('Album', [
    'name' => 'Rock or Bust',
    'artist' => 'AC/DC'
]);

将其视为您对未来生成的专辑的定义——例如,当您这样做时

use Laracasts\TestDummy\Factory;

$album = Factory::create('Album');

Faker

您可能不希望为各种工厂硬编码字符串。使用随机数据会更简单、更快。TestDummy 引入了出色的 Faker 库来协助此操作。

实际上,您 tests/factories/ 目录中的任何文件都将自动访问一个 $faker 对象,您可以使用它。以下是一个示例

$factory('Comment', [
    'body' => $faker->sentence
]);

现在,每次生成新的评论时,body 字段将被设置为随机句子。请参阅 Faker 文档以获取大量可用的模拟。

关系

如果您希望,TestDummy 还可以自动生成您的关系模型。您只需让 TestDummy 知道其关联模型类型即可。TestDummy 将自动为您构建和保存该关系!

使用上面提到的 Comment 例子,一个评论属于一个用户,对吧?让我们设置一下

$factory('Comment', [
    'user_id' => 'factory:User',
    'body' => $faker->sentence
]);

就这样!注意这里的特殊语法:“factory:”,后面跟关联类/模型的名称。

为了进一步说明,如果一首歌属于一张专辑,而一张专辑属于一位艺术家,那么我们可以轻松地表示这一点

$factory('App\Song', [
    'album_id' => 'factory:App\Album',
    'name' => $faker->sentence
]);

$factory('App\Album', [
    'artist_id' => 'factory:App\Artist',
    'name' => $faker->word
]);

$factory('App\Artist', [
    'name' => $faker->word
]);

所以,这里有趣的是:这一切都将递归工作。换句话说,如果您这样做...

use Laracasts\TestDummy\Factory;

$song = Factory::create('App\Song');

...TestDummy 不仅会构建并持久化一个歌曲到数据库中,还会对相关的专辑及其相关的艺术家执行相同的操作。很酷!

自定义工厂

到目前为止,你已经学会了如何使用类名生成数据,例如使用App\User。然而,有时你可能会需要为了测试目的定义多种用户类型。

虽然你可以使用覆盖,如下所示

Factory::create('App\User', ['role' => 'admin']);

...如果你经常这样做,创建一个自定义工厂,如下所示

// A generic factory for users...

$factory('App\User', [
    'username' => $faker->username,
    'password' => $faker->password,
    'role'     => 'member'
]);

// And a custom one for administrators

$factory('App\User', 'admin_user', [
    'username' => $faker->username,
    'password' => $faker->password,
    'role'     => 'admin'
]);

在上面的代码片段中,你已经熟悉了第一个例子。对于第二个例子,注意我们已经添加了一个“简称”,或标识符,用于这种特殊类型的用户工厂。现在,每当你想要快速生成一个管理员用户时,你可以这样做

use Laracasts\TestDummy\Factory;

$adminUser = Factory::create('admin_user');

使用闭包定义

或者,你也可以将闭包作为第二个参数传递给$factory方法。这在需要更多控制每个属性所分配的值的场景中非常有用。以下是一个示例

$factory('App\Artist', function($faker) {
    $name = sprintf('Some Band Named %s', $faker->word);
    
    return [
        'name' => $name
    ];
});

当然,请确保从这个闭包中返回一个数组。如果不这样做,将会抛出异常。

第3步:设置

在测试数据库时,建议每个测试都使用完全相同的数据库环境和结构。这样,你可以防止出现误判。在这种情况下,SQLite数据库(甚至一个内存中的数据库)是一个不错的选择。

public function setUp()
{
    parent::setUp();

    Artisan::call('migrate');
}

或者,如果内存中的数据库不可行,为了节省时间,这个包中包含了一个辅助的Laracasts\TestDummy\DbTestCase类。如果你扩展它,在每次测试之前,你的测试数据库将进行迁移(如果需要),并且所有数据库修改将通过一个事务进行,然后在tearDown时回滚。这将提高你的测试速度,并确保所有测试都从相同的数据库结构开始。

use Laracasts\TestDummy\DbTestCase;

class ExampleTest extends DbTestCase {

    /** @test */
    function it_does_something()
    {
        // Before each test, your database will be rolled back
    }
}

第4步:编写你的测试

你现在可以开始测试了。这里有一些代码可以帮助你入门。假设你已经创建了一个PostComment模型...

use Laracasts\TestDummy\Factory;

$comment = Factory::create('Comment');

这将创建并保存一个Comment和一个Post记录到数据库中。

或者,你可能需要编写一个测试来确保,如果你有三个带有其各自长度的歌曲,当你调用拥有者的Album模型上的getTotalLength方法时,它将返回正确的值。这很简单!

// create three songs, and explicitly set the length
Factory::times(3)->create('Song', ['length' => 200]);

$album = Album::first(); // this will be created once automatically.

$this->assertEquals(600, $album->getTotalLength());

现在,当然,请确保你在你的工厂文件中为SongAlbum注册了一个定义,然后你就准备好了!

// tests/factories/factories.php

$factory('Song', [
  'album_id' => 'factory:Album',
  'name' => $faker->sentence
]);

$factory('Album', [
  'name' => $faker->sentence
]);

常见问题解答

如何指定不同的工厂文件夹?

很简单。在测试运行之前,添加

Factory::$factoriesPath = 'app/tests/factories';

现在,TestDummy将在这个app/tests/factories文件夹中查找你注册的工厂。

我想控制我的模型是如何构建和保存的...

好的,只需创建你自己的Laracasts\TestDummy\IsPersistable实现。这个合约由一些你需要实现的方法组成。

一旦你有了你的实现,在测试运行之前,添加

Factory::$databaseProvider = new MyCustomBuilder;

就这样!现在,每当TestDummy生成并保存一个实体时,它将参考你的自定义实现。