测试

未匹配的标注
本文档最新版为 6.x,旧版本可能放弃维护,推荐阅读最新版!

测试#

简介#

Lumen 在创建的时候就考虑到了测试部分。 事实上,Lumen 默认就支持使用 PHPunit 来做单元测试,并为你的应用创建好了 phpunit.xml 文件。框架还提供了一些便利的辅助函数,让你可以直观的测试应用的 JSON 响应。

tests 目录中提供了一个 ExampleTest.php 的示例文件。安装新的 Lumen 应用程序后, 只需在命令行上执行 phpunit 就可以测试你的用例。

测试环境#

在运行测试时,Lumen 自动将缓存驱动配置为 array ,这意味着在测试中将不会保存任何缓存数据。

你可以随意地创建必要的测试环境配置。testing 环境变量可以在 phpunit.xml 中进行配置。

定义和运行测试#

要创建一个测试用例,只需在 tests 文件中创建一个新的测试文件。
这个测试类应该继承 TestCase 。接着就可以像平常使用 PHPUnit 一样来定义测试方法。要运行测试用例,只需在命令行中执行 phpunit 命令:

<?php

class FooTest extends TestCase
{
    public function testSomethingIsTrue()
    {
        $this->assertTrue(true);
    }
}

注意:如果在测试类中自定义了 setUp 方法,请确保调用 parent::setUp 方法。

测试应用程序#

Lumen 提供了非常好用的 API,用于向应用程序发送 HTTP 请求和验证输出结果。

测试 JSON APIs#

Lumen 也提供了几个辅助函数来测试 JSON API 及其响应。例如:getpostputpatchdelete 方法可以用于发出各种 HTTP 请求动作。你也可以很简单为这些方法添加数据和头信息。首先,让我们来编写一个测试,将 POST 请求发送到 /user ,并断言其会返回 JSON 格式的指定数组:

<?php

class ExampleTest extends TestCase
{
    /**
     * 一个基本功能测试用例
     *
     * @return void
     */
    public function testBasicExample()
    {
        $this->json('POST', '/user', ['name' => 'Sally'])
             ->seeJson([
                'created' => true,
             ]);
    }
}

seeJson 方法将传入数组转换成 JSON,并验证改 JSON 片段是否存在于应用程序返回的 JSON 响应中的 任何位置 。因此,如果有其它属性在 JSON 响应中,但是只要指定的片段存在,此测试便会通过。

验证完全匹配的 JSON#

如果你想验证传入的数组与应用程序凡返回的 JSON 是否 完全 匹配
,你应该使用 seeJsonEquals 方法:

<?php

class ExampleTest extends TestCase
{
    /**
     * 一个简单的测试例子
     *
     * @return void
     */
    public function testBasicExample()
    {
        $this->post('/user', ['name' => 'Sally'])
             ->seeJsonEquals([
                'created' => true,
             ]);
    }
}

认证#

actingAs 辅助函数提供了一个简单的方法来让指定的用户认证为当前用户:

<?php

class ExampleTest extends TestCase
{
    public function testApplication()
    {
        $user = factory('App\User')->create();

        $this->actingAs($user)
             ->get('/user');
    }
}

自定义 HTTP 请求#

如果你想自定义一个 HTTP 请求到你的应用程序上,并且得到完整的 Illuminate\Http\Response 对象,你可以使用 call 方法:

public function testApplication()
{
    $response = $this->call('GET', '/');

    $this->assertEquals(200, $response->status());
}

如果你想构造 POST, PUT, 或者 PATCH 请求,并且在请求时传入一个数组作为请求参数。当然,你也可以在路由或者控制器中通过 请求实例 来获取传递的参数:

$response = $this->call('POST', '/user', ['name' => 'Taylor']);

使用数据库#

Lumen 提供了多种方便的工具让你来测试使用数据库的应用程序。首先,你可以使用 seeInDatabase 辅助函数来断言数据库中是否存在与指定条件相匹配的数据。例如:如果我们想验证 users 表中是否存在 email 字段的为 sally@example.com 的数据,我们按照以下方式进行测试:

public function testDatabase()
{
    // Make call to application...

    $this->seeInDatabase('users', ['email' => 'sally@foo.com']);
}

当然, 使用 seeInDatabase 方法和其他辅助函数只是为了方便。你可以随便使用 PHPUnit 内置的断言方法来来扩充测试。

每次测试后重置数据库#

每次测试完重置数据库非常有用,因为这样做,以前测试的数据不会干扰后续的测试。

使用迁移#

其中一种方式是在测试完成后还原数据库,在下一次测试前进行数据迁移。Lumen 提供了一个简单 DatabaseMigrations trait ,它会自动帮你处理好这些操作。你只需在测试中使用此 trait 即可:

<?php

use Laravel\Lumen\Testing\DatabaseMigrations;
use Laravel\Lumen\Testing\DatabaseTransactions;

class ExampleTest extends TestCase
{
    use DatabaseMigrations;

    /**
     * 一个基本功能测试例子。
     *
     * @return void
     */
    public function testBasicExample()
    {
        $this->get('/foo');
    }
}

使用事物#

另一种选择是,将每个测试用例包含在数据库的事物中。Lumen 提供了一个很便利的 DatabaseTransactions trait ,它会将自动处理这些:

<?php

use Laravel\Lumen\Testing\DatabaseMigrations;
use Laravel\Lumen\Testing\DatabaseTransactions;

class ExampleTest extends TestCase
{
    use DatabaseTransactions;

    /**
     * 一个基础的测试例子。
     *
     * @return void
     */
    public function testBasicExample()
    {
        $this->get('/foo');
    }
}

模型工厂#

测试时,常常会在测试前将数据写入数据库中。创建数据时,除了手动来设置每个字段外,还可以使用 Eloquent 模型 的「 工厂 」来设置每个属性的默认值。在开始使用前,可以先看看应用程序中的 database/factories/ModelFactory.php 文件。这个文件中包含一个现成的工厂定义:

$factory->define('App\User', function ($faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
    ];
});

在闭包内是工厂的定义,你可以返回模型中所有属性的默认测试值。这个闭包将会接收一个 Faker PHP 函数库的实例,它可以让你很方便的生成各种随机数据以进行测试。

当然,你也可以随意将自定义的工厂添加到 ModelFactory.php 文件中。

多个工厂类型#

有些时候你希望针对同一个 Eloquent 模型来创建多个工厂。例如:除了一般员工工厂外,还有「管理员」工厂。你可以使用 defineAs 方法来定义这个工厂:

$factory->defineAs('App\User', 'admin', function ($faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
        'admin' => true,
    ];
});

除了从一般用户复制属性,你也可以使用 raw 方法获取所有属性。一旦拥有这些属性,就可以简单地为其添加额外值:

$factory->defineAs('App\User', 'admin', function ($faker) use ($factory) {
    $user = $factory->raw('App\User');

    return array_merge($user, ['admin' => true]);
});

在测试中使用工厂#

在定义工厂后,就可以在测试或数据填充中,通过全局 factory 方法生成模型实例。接下来,让我们来看几个创建模型实例的例子。首先,我们将使用 make 方法创建模型,但不会将模型保存到数据库:

public function testDatabase()
{
    $user = factory('App\User')->make();

    // 在测试中使用模型
}

如果你想重写模型中一些默认值,你可以传递一个包含数值的数组至 make 方法。只有指定的值会被替换,其他剩余的值则会按照工厂指定的默认值来设定:

$user = factory('App\User')->make([
    'name' => 'Abigail',
]);

你也可以创建包含多个模型的集合,或者创建一个指定类型的模型:

// 创建三个 App\User 实例
$users = factory('App\User', 3)->make();

// 创建 App\User 「admin」 实例
$user = factory('App\User', 'admin')->make();

// 创建三个 App\User 「admin」 实例
$users = factory('App\User', 'admin', 3)->make();

保存工厂模型#

create 方法不仅仅能创建模型实例,也可以使用 Eloquent 的 save 方法将它们保存到数据库:

public function testDatabase()
{
    $user = factory('App\User')->create();

    // 在测试中使用模型
}

同样的,你也可以在数据传递给 create 方法时重写模型属性:

$user = factory('App\User')->create([
    'name' => 'Abigail',
]);

增加关联至模型#

甚至,你可以将多个模型实例保存到数据。在本例中,我们还会增加关联至我们所创建的模型。当使用 create 方法创建多个模型时,它会返回一个 Eloquent 集合实例 ,让你能使用集合提供的便利方法,例如 each 方法:

$users = factory('App\User', 3)
           ->create()
           ->each(function($u) {
                $u->posts()->save(factory('App\Post')->make());
            });

模拟#

模拟事件#

如果你大量地使用 Lumen 的事件系统,你可能会希望在测试时停止或者模拟模拟某些事件。例如,如果你在测试你的注册功能,你可能不希望所有的 UserRegistered 事件被触发,因为它们会触发欢迎邮件的发送。

Lumen 提供了一个便利的 expectsEvents 方法,它会验证被期望的事件是否被触发,可防止该事件的任何处理进程被运行:

<?php

class ExampleTest extends TestCase
{
    public function testUserRegistration()
    {
        $this->expectsEvents('App\Events\UserRegistered');

        // 测试用户注册代码
    }
}

如果你想阻止所有的事件处理程序运行,你可以使用 withoutEvents 方法:

<?php

class ExampleTest extends TestCase
{
    public function testUserRegistration()
    {
        $this->withoutEvents();

        // 测试用户注册代码……
    }
}

模拟任务#

有时候,当向你的应用程序发起请求时,你可能希望简单地测试通过控制器分派的特定任务。这允许你与任务逻辑分开,单独地测试路由或控制器。

Lumen 提供了一个便利的 expectsJobs 方法,它可以验证期望的任务是否被分派,但是任务自身不会被执行:

<?php

class ExampleTest extends TestCase
{
    public function testPurchasePodcast()
    {
        $this->expectsJobs('App\Jobs\PurchasePodcast');

        // 测试购买播客代码……
    }
}

注意:这个方法只会检查通过全局助手函数 dispatch 或者从路由或控制器中 $this->dispatch 方法分派的任务。它不会检查直接发送到 Queue::push 的任务。

模拟 Facades#

在测试时,经常需要模拟对 Lumen facade 的调用。例如,考虑如下控制器的操作:

<?php

namespace App\Http\Controllers;

use Cache;

class UserController extends Controller
{
    /**
     * 展示应用程序所有用户的列表
     *
     * @return Response
     */
    public function index()
    {
        $value = Cache::get('key');

        //
    }
}

我们可以通过 shouldReceive 方法模拟调用 Cache facade,它会返回一个 Mockery 的实例。实际上 facade 被 Lumen 的 服务容器 解析和管理,它们比起一般的静态类更有可测性。例如:我们模拟调用 Cache facade:

<?php

class FooTest extends TestCase
{
    public function testGetIndex()
    {
        Cache::shouldReceive('get')
                    ->once()
                    ->with('key')
                    ->andReturn('value');

        $this->get('/users');
    }
}

注意: 你不应该模拟 Request facade。应该在测试中使用 callpost 这样的 HTTP 辅助函数来传递你想要的数据。

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/lumen/5.5/test/...

译文地址:https://learnku.com/docs/lumen/5.5/test/...

上一篇 下一篇
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
贡献者:7
讨论数量: 0
发起讨论 只看当前版本


暂无话题~