测试

未匹配的标注
本文档最新版为 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/...

上一篇 下一篇
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
贡献者:7
讨论数量: 1
发起讨论 只看当前版本


zeroChan
这里有个错别字?
0 个点赞 | 0 个回复 | 问答 | 课程版本 5.6