应用程序测试
介绍
Laravel 提供了一个非常流畅的 API,用于向您的应用程序发出 HTTP 请求、检查输出,甚至填写表单。例如,请查看下面定义的测试:
<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
/**
* 一个基本的功能测试示例。
*
* @return void
*/
public function testBasicExample()
{
$this->visit('/')
->see('Laravel 5')
->dontSee('Rails');
}
}
visit
方法向应用程序发出 GET
请求。see
方法断言我们应该在应用程序返回的响应中看到给定的文本。dontSee
方法断言给定的文本未在应用程序响应中返回。这是 Laravel 中最基本的应用程序测试。
您还可以使用 visitRoute
方法通过命名路由发出 GET
请求:
$this->visitRoute('profile');
$this->visitRoute('profile', ['user' => 1]);
与应用程序交互
当然,您可以做的不仅仅是断言给定响应中出现的文本。让我们看看一些点击链接和填写表单的示例:
与链接交互
在此测试中,我们将向应用程序发出请求,在返回的响应中“点击”一个链接,然后断言我们到达了给定的 URI。例如,假设我们的响应中有一个文本值为“关于我们”的链接:
<a href="/about-us">关于我们</a>
现在,让我们编写一个测试来点击链接并断言用户到达了正确的页面:
public function testBasicExample()
{
$this->visit('/')
->click('关于我们')
->seePageIs('/about-us');
}
您还可以使用 seeRouteIs
方法检查用户是否到达了正确的命名路由:
->seeRouteIs('profile', ['user' => 1]);
与表单交互
Laravel 还提供了几种方法来测试表单。type
、select
、check
、attach
和 press
方法允许您与表单的所有输入进行交互。例如,假设此表单存在于应用程序的注册页面上:
<form action="/register" method="POST">
{{ csrf_field() }}
<div>
姓名: <input type="text" name="name">
</div>
<div>
<input type="checkbox" value="yes" name="terms"> 接受条款
</div>
<div>
<input type="submit" value="注册">
</div>
</form>
我们可以编写一个测试来完成此表单并检查结果:
public function testNewUserRegistration()
{
$this->visit('/register')
->type('Taylor', 'name')
->check('terms')
->press('注册')
->seePageIs('/dashboard');
}
当然,如果您的表单包含其他输入,例如单选按钮或下拉框,您也可以轻松填写这些类型的字段。以下是每种表单操作方法的列表:
方法 | 描述 |
---|---|
$this->type($text, $elementName) | 在给定字段中“输入”文本。 |
$this->select($value, $elementName) | “选择”单选按钮或下拉字段。 |
$this->check($elementName) | “选中”复选框字段。 |
$this->uncheck($elementName) | “取消选中”复选框字段。 |
$this->attach($pathToFile, $elementName) | 将文件“附加”到表单。 |
$this->press($buttonTextOrElementName) | 按下具有给定文本或名称的按钮。 |
文件输入
如果您的表单包含 file
输入,您可以使用 attach
方法将文件附加到表单:
public function testPhotoCanBeUploaded()
{
$this->visit('/upload')
->attach($pathToFile, 'photo')
->press('上传')
->see('上传成功!');
}
测试 JSON API
Laravel 还提供了几种用于测试 JSON API 及其响应的助手。例如,json
、get
、post
、put
、patch
和 delete
方法可用于发出具有各种 HTTP 动词的请求。您还可以轻松地将数据和头信息传递给这些方法。首先,让我们编写一个测试来向 /user
发出 POST
请求并断言返回了预期的数据:
<?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 是精确匹配的,您应该使用 seeJsonEquals
方法:
<?php
class ExampleTest extends TestCase
{
/**
* 一个基本的功能测试示例。
*
* @return void
*/
public function testBasicExample()
{
$this->json('POST', '/user', ['name' => 'Sally'])
->seeJsonEquals([
'created' => true,
]);
}
}
验证结构匹配
还可以验证 JSON 响应是否符合特定结构。在这种情况下,您应该使用 seeJsonStructure
方法并传递预期的 JSON 结构:
<?php
class ExampleTest extends TestCase
{
/**
* 一个基本的功能测试示例。
*
* @return void
*/
public function testBasicExample()
{
$this->get('/user/1')
->seeJsonStructure([
'name',
'pet' => [
'name', 'age'
]
]);
}
}
上面的示例说明了期望接收到一个 name
属性和一个嵌套的 pet
对象,其中包含自己的 name
和 age
属性。seeJsonStructure
不会因为响应中存在其他键而失败。例如,如果 pet
具有 weight
属性,测试仍将通过。
您可以使用 *
来断言返回的 JSON 结构中有一个列表,其中每个列表项至少包含在值集中找到的属性:
<?php
class ExampleTest extends TestCase
{
/**
* 一个基本的功能测试示例。
*
* @return void
*/
public function testBasicExample()
{
// 断言列表中的每个用户至少具有 id、name 和 email 属性。
$this->get('/users')
->seeJsonStructure([
'*' => [
'id', 'name', 'email'
]
]);
}
}
您还可以嵌套 *
符号。在这种情况下,我们将断言 JSON 响应中的每个用户包含一组给定的属性,并且每个用户的每个宠物也包含一组给定的属性:
$this->get('/users')
->seeJsonStructure([
'*' => [
'id', 'name', 'email', 'pets' => [
'*' => [
'name', 'age'
]
]
]
]);
会话 / 认证
Laravel 提供了几个用于在测试期间处理会话的助手。首先,您可以使用 withSession
方法将会话数据设置为给定数组。这对于在向应用程序发出请求之前加载会话数据非常有用:
<?php
class ExampleTest extends TestCase
{
public function testApplication()
{
$this->withSession(['foo' => 'bar'])
->visit('/');
}
}
当然,会话的一个常见用途是维护已认证用户的状态。actingAs
辅助方法提供了一种简单的方法来将给定用户认证为当前用户。例如,我们可以使用模型工厂生成并认证一个用户:
<?php
class ExampleTest extends TestCase
{
public function testApplication()
{
$user = factory(App\User::class)->create();
$this->actingAs($user)
->withSession(['foo' => 'bar'])
->visit('/')
->see('Hello, '.$user->name);
}
}
您还可以通过将守卫名称作为第二个参数传递给 actingAs
方法来指定应使用哪个守卫来认证给定用户:
$this->actingAs($user, 'api')
禁用中间件
在测试应用程序时,您可能会发现禁用某些测试的中间件很方便。这将允许您在不考虑中间件的情况下测试路由和控制器。Laravel 包含一个简单的 WithoutMiddleware
trait,您可以使用它来自动禁用测试类的所有中间件:
<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
use WithoutMiddleware;
//
}
如果您只想为几个测试方法禁用中间件,可以在测试方法中调用 withoutMiddleware
方法:
<?php
class ExampleTest extends TestCase
{
/**
* 一个基本的功能测试示例。
*
* @return void
*/
public function testBasicExample()
{
$this->withoutMiddleware();
$this->visit('/')
->see('Laravel 5');
}
}
自定义 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']);
PHPUnit 断言
Laravel 为 PHPUnit 测试提供了多种自定义断言方法:
方法 | 描述 |
---|---|
->assertResponseOk(); | 断言客户端响应具有 OK 状态码。 |
->assertResponseStatus($code); | 断言客户端响应具有给定代码。 |
->assertViewHas($key, $value = null); | 断言响应视图具有给定的绑定数据。 |
->assertViewHasAll(array $bindings); | 断言视图具有给定的绑定数据列表。 |
->assertViewMissing($key); | 断言响应视图缺少某个绑定数据。 |
->assertRedirectedTo($uri, $with = []); | 断言客户端是否被重定向到给定的 URI。 |
->assertRedirectedToRoute($name, $parameters = [], $with = []); | 断言客户端是否被重定向到给定的路由。 |
->assertRedirectedToAction($name, $parameters = [], $with = []); | 断言客户端是否被重定向到给定的操作。 |
->assertSessionHas($key, $value = null); | 断言会话具有给定的值。 |
->assertSessionHasAll(array $bindings); | 断言会话具有给定的值列表。 |
->assertSessionHasErrors($bindings = [], $format = null); | 断言会话绑定了错误。 |
->assertHasOldInput(); | 断言会话具有旧输入。 |
->assertSessionMissing($key); | 断言会话缺少给定的键。 |