测试
测试
简介
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 及其响应。例如:get
、post
、put
、patch
和 delete
方法可以用于发出各种 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。应该在测试中使用call
和post
这样的 HTTP 辅助函数来传递你想要的数据。
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
推荐文章: