测试

未匹配的标注

测试

简介

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 API 接口

Lumen 同样提供了一些辅助函数用于测试 JSON API 及其响应。例如,get,post,put,patch 和 delete 方法可以被用于发起各种方式的 HTTP 请求。同样,你也可以很轻松地给这些方法传递数据或者头部信息。首先,我们要创建一个测试,向 /user 发起 POST 请求,并且断言以 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 请求,可以在请求时传入一个数组作为请求参数。当然,你也可以在路由及控制器中通过 Request 实例 来获取传过来的参数:

   $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;

    /**
     * A basic functional test example.
     *
     * @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;

    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function testBasicExample()
    {
        $this->get('/foo');
    }
}

模型工厂

在测试时,通常需要在执行测试之前将少量的数据记录插入到数据库。Lumen 允许您使用「factories」为每个 Eloquent 模型定义一组默认属性,而不是在创建这些数据时手动指定每一列的值。首先,请查看应用中的 database/factories/ModelFactory.php 文件。开箱即用,此文件包含一个工厂定义:

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

在作为工厂定义的闭包中,您可以返回模型上所有属性的默认测试值。 闭包将接收 Faker PHP 库的一个实例,它允许您方便地生成各种随机数据以用于测试。

当然,您可以自由地将自己的附加工厂添加到 ModelFactory.php 文件中 。

多种工厂类型

有时您可能希望同一个 Eloquent 模型有多种工厂。例如,也许您希望除了普通的用户之外还为「Administrator」用户提供工厂。您可以使用 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',
   ]);

你还可以创建许多模型的集合或创建给定类型的模型:

// Create three App\User instances...
$users = factory('App\User', 3)->make();

// Create an App\User "admin" instance...
$user = factory('App\User', 'admin')->make();

// Create three App\User "admin" instances...
$users = factory('App\User', 'admin', 3)->make();

持久化工厂模型

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

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

    // Use model in tests...
}

同样,你也可以通过将数组传递给 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();

        // Test user registration code...
    }
}

模拟任务

有时,你可能希望在向应用发送请求时,简单的测试控制器是否调度了特定的任务(Jobs)。这允许你隔离测试路由或控制器——与你的任务(Job)逻辑分开。当然,此后你可以在一个单独的测试类中测试该任务(Job)。

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 mock 的实例。因为门面实际上由 Lumen 的 服务容器 解析和管理,它们比典型的静态类更具有可测性。例如,让我们模拟调用 Cache 门面:

<?php

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

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

注意: 你不应该模拟 Request 门面。应该在测试时使用如 call 及 post 这样的 HTTP 辅助函数来传递你想要的数据。

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

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

原文地址:https://learnku.com/docs/lumen/6.x/testi...

译文地址:https://learnku.com/docs/lumen/6.x/testi...

上一篇 下一篇
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
贡献者:2
讨论数量: 0
发起讨论 查看所有版本


暂无话题~