samuelgfeller/test-traits

PHPUnit 测试特性集合

6.1.1 2024-05-02 14:48 UTC

This package is auto-updated.

Last update: 2024-09-15 16:28:41 UTC


README

这是 selective/test-traits 的一个副本,包含额外的测试特性,包括一个 fixture 特性,可以轻松插入与测试用例相关的自定义数据,以及一个 SQL 模式生成器,用于设置集成测试的测试数据库。

Latest Version on Packagist Software License Build Status Total Downloads

需求

  • PHP 8.2+
  • Composer
  • PHPUnit 10+

安装

composer require samuelgfeller/test-traits --dev

文档

测试环境设置: 测试设置
使用这些特性编写测试: 编写测试

从 selective/test-traits 迁移

如果你一直在使用 selective/test-traits 包,并想迁移到这个库,以下函数需要重命名

  • 函数 insertFixtures(带 "s")现在为 insertDefaultFixtureRecords
  • 旧的 insertFixture 函数现在为 insertFixtureRow,而 insertFixture 是另一个提供灵活方式插入具有可选自定义属性的 fixtures 的函数。

否则,特性是相同的,可以以相同的方式使用。此包将与 selective/test-traits 保持同步。

FixtureTestTrait

一个用于创建和插入具有可在测试函数中定义的数据的 fixtures 的特性。

提供的方法

/**
 * @param class-string $fixture
 * @param array $attributes
 */
protected function insertFixture(string $fixture, array $attributes = []): array

用法

// Inserts the fixture with the first_name being "Bob" and the rest default values from the fixture.
// Returns the inserted row data with the auto-incremented id.
$bobUserRow = $this->insertFixture(UserFixture::class, ['first_name' => 'Bob']);

Fixture

每个 fixture 都必须有一个属性 $table,包含表名,以及一个数组 $records,包含要插入的默认数据。

<?php

namespace App\Test\Fixture;

class UserFixture
{
    // Database table name
    public string $table = 'user';

    // Database records
    public array $records = [
        // Only one record is relevant as the data is defined in the test function
        [
            // The id 
            'id' => 1,
            'first_name' => 'John',
            'last_name' => 'Doe',
        ],
    ];
}

使用自定义属性插入 fixture

要定义应覆盖 fixture 类默认值的自定义数据,可以使用 insertFixture() 函数。
第一个参数是 fixture 完整限定类名,例如 UserFixture::class,第二个(可选)是一个属性数组。

属性数组包含一行数据库的数据,例如:
['field_name' => 'value', 'other_field_name' => 'other_value'].

可以将多个属性数组传递给函数,以插入具有不同数据的多个行,如下所示。

不需要在属性数组中指定表的所有字段。对于未指定的字段,将使用 fixture 的第一个 $records 条目的值。

该函数返回一个包含从 fixture 插入的数据的数组,包括自增的 id 或当传递多个行时,每行插入的数组。

<?php

namespace App\Test\TestCase;

use PHPUnit\Framework\TestCase;
use TestTraits\Trait\FixtureTestTrait;

final class FetchUsersTestAction extends TestCase
{
    // ...
    use FixtureTestTrait;

    public function testAction(): void
    {
        // Insert the fixture with the default values
        $defaultUserRow = $this->insertFixture(UserFixture::class);
        // $defaultUserRow equals ['id' => 1, 'first_name' => 'John', 'last_name' => 'Doe']
        
        // Insert the fixture with the given attributes
        $bobUserRow = $this->insertFixture(UserFixture::class, ['first_name' => 'Bob', ]);
        // $bobUserRow equals ['id' => 2, 'first_name' => 'Bob', 'last_name' => 'Doe']
        
        // Insert 2 rows with the given attributes 
        $jackAndAliceRows = $this->insertFixture(
            UserFixture::class, 
            ['first_name' => 'Jack', 'last_name' => 'Brown'], 
            ['first_name' => 'Alice']
        );
        // $jackAndAliceRows contains the two inserted rows:
        // [
        //      ['id' => 3, 'first_name' => 'Jack', 'last_name' => 'Brown'],
        //      ['id' => 4, 'first_name' => 'Alice', 'last_name' => 'Doe']
        // ]
        
        // Multiple rows can also be inserted when passing one attribute argument that contains
        // multiple arrays for each row
        $benAndEveRows = $this->insertFixture(
            UserFixture::class, [
                ['first_name' => 'Jack', 'last_name' => 'Brown'], 
                ['first_name' => 'Alice']
            ]
        );
        
        // ...
    }
}

FixtureTestTrait 使用 DatabaseTestTrait 与数据库交互。

HttpTestTrait

需求

  • 任何 PSR-7 和 PSR-17 工厂实现。
composer require nyholm/psr7-server
composer require nyholm/psr7

提供的方法

  • createRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface
  • createFormRequest(string $method, $uri, array $data = null): ServerRequestInterface
  • createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface

用法

<?php

namespace App\Test\TestCase;

use PHPUnit\Framework\TestCase;
use TestTraits\Trait\ContainerTestTrait;
use TestTraits\Trait\HttpTestTrait;

class GetUsersTestAction extends TestCase
{
    use ContainerTestTrait;
    use HttpTestTrait;
     
    public function test(): void
    {
        $request = $this->createRequest('GET', '/api/users');
        $response = $this->app->handle($request);
        $this->assertSame(200, $response->getStatusCode());
    }
}

创建具有查询字符串参数的请求

$request = $this->createRequest('GET', '/api/users')
    ->withQueryParams($queryParams);

RouteTestTrait

Slim 4 框架路由测试特性。

需求

  • Slim 4 框架应用

提供的方法

urlFor(string $routeName, array $data = [], array $queryParams = []): string

用法

<?php

namespace App\Test\TestCase;

use PHPUnit\Framework\TestCase;
use TestTraits\Trait\ContainerTestTrait;
use TestTraits\Trait\HttpTestTrait;
use TestTraits\Trait\RouteTestTrait;

final class GetUsersTestAction extends TestCase
{
    use ContainerTestTrait;
    use HttpTestTrait;
    use RouteTestTrait;
     
    public function test(): void
    {
        $request = $this->createRequest('GET', $this->urlFor('get-users'));
        $response = $this->app->handle($request);
        $this->assertSame(200, $response->getStatusCode());
    }
}

使用命名路由和查询字符串参数创建请求

$request = $this->createRequest('GET',  $this->urlFor('get-users'))
    ->withQueryParams($queryParams);

MailerTestTrait

需求:composer require symfony/mailer

DI 容器设置示例

use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Mailer\EventListener\EnvelopeListener;
use Symfony\Component\Mailer\EventListener\MessageListener;
use Symfony\Component\Mailer\EventListener\MessageLoggerListener;
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mailer\Transport;
use Symfony\Component\Mailer\Transport\TransportInterface;
// ...

return [
    // Mailer
    MailerInterface::class => function (ContainerInterface $container) {
        return new Mailer($container->get(TransportInterface::class));
    },

    // Mailer transport
    TransportInterface::class => function (ContainerInterface $container) {
        $settings = $container->get('settings')['smtp'];

        // smtp://user:pass@smtp.example.com:25
        $dsn = sprintf(
            '%s://%s:%s@%s:%s',
            $settings['type'],
            $settings['username'],
            $settings['password'],
            $settings['host'],
            $settings['port']
        );

        $eventDispatcher = $container->get(EventDispatcherInterface::class);

        return Transport::fromDsn($dsn, $eventDispatcher);
    },

    EventDispatcherInterface::class => function () {
        $eventDispatcher = new EventDispatcher();
        $eventDispatcher->addSubscriber(new MessageListener());
        $eventDispatcher->addSubscriber(new EnvelopeListener());
        $eventDispatcher->addSubscriber(new MessageLoggerListener());

        return $eventDispatcher;
    },
    
    // ...
],

用法

PHPUnit 测试用例

<?php

namespace App\Test\TestCase;

use PHPUnit\Framework\TestCase;
use TestTraits\Trait\ContainerTestTrait;
use TestTraits\Trait\MailerTestTrait;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;

final class MailerExampleTest extends TestCase
{
    use ContainerTestTrait;
    use MailerTestTrait;

    public function testMailer(): void
    {
        $mailer = $this->container->get(MailerInterface::class);

        // Send email
        $email = (new Email())
            ->from('hello@example.com')
            ->to('you@example.com')
            ->subject('Time for Symfony Mailer!')
            ->text('Sending emails is fun again!')
            ->html('<p>My HTML content</p>');

        $mailer->send($email);

        $this->assertEmailCount(1);
        $this->assertEmailTextBodyContains($this->getMailerMessage(), 'Sending emails is fun again!');
        $this->assertEmailHtmlBodyContains($this->getMailerMessage(), '<p>My HTML content</p>');
    }
}

许可证

本项目采用MIT许可协议 — 详细信息请参阅许可文件