laravel 基础教程 —— 测试

测试

简介

测试是 Laravel 打造的宗旨绪念。事实上,Laravel 开箱即用的辅助 PHPUnit
测试,你的利用的根目录包罗了 phpunit.xml 文件。同时,Laravel
也顺手了一部分惠及的辅助方法可以使你充足应用的测试。

tests 目录中提供了一个 ExampleTest.php 文件。在装置到位 Laravel
应用之后,你只需求在根目录运行 phpunit 命令就可以执行测试。

测试环境

当运行测试时,Laravel 会自动的装置配置环境为 testing。同时,Laravel
会自动的配备 session 和 缓存为 array
驱动。那象征会话或者缓存数据在测试时期不会被持久化。

如若您须求,你完全可以友善成立一个测试环境。testing 环境变量是在
phpunit.xml 文件中被安插的,可是你要保管在运转测试此前先运行
config:clear Artisan 命令来驱除配置缓存。

概念 & 运行测试

您可以利用 make:test Artisan 命令来创制一个测试用例:

php artisan make:test UserTest

以此命令会在 tests 目录下创办一个新的 UserTest 类。你可以像使用
PHPUnit 一样接着在类中定义一些测试方法。然后您只必要在终端履行 phpunit
命令就能够运作测试:

<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrationgs;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class UserTest extends TestCase
{
  /**
   * A basic test example.
   *
   * @return void
   */
   public function testExample()
   {
     $this->assetTrue(true);
   }
}

小心:要是您在测试用例中定义了和睦的 setUp 方法,你要求确保优先调用
parent::setUp 方法。

使用测试

Laravel 提供丰裕百步穿杨的 API 来创设 HTTP
请求检查输出甚至是填写表格。比如,让咱们看下 tests 目录下的
ExampleTest.php 文件:

<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class ExampleTest extends TestCase
{
  /**
   * A basic functional test example
   *
   * @return void
   */
   public function testBasicExample()
   {
     $this->visit('/')
          ->see('Laravel 5')
          ->dontSee('Rails');
   }
}

visit 方法可以在运用中布局一个 GET 请求。see
方法会断言大家理应从使用的响应中看出所给定的文件。dontSee 方法刚好与
see 相反,它预感大家不应有看到给定的公文。那就是 Laravel
提供的最要旨的应用测试。

与应用举办交互

理所当然,你能够比那种简易的预知响应给定的文本做的越来越多。让大家来看一些点击链接和填充表单的例子:

点击超链

在这一个测试,我们会为使用构造一个呼吁,在响应中会重返一个可点击的超链,并且大家会断言它应有导向给定的
URL。比如,让大家若是应用中回到了一个文件为 “About Us” 的超链:

<a href="/about-us">About Us</a>

今天,让我们编辑一个测试并断言点击链接会跳转到相应的页面:

public function testBasicExample()
{
  $this->visit('/')
       ->click('About Us')
       ->seePageIs('/about-us')
}

与表单交互

Laravel
同样也提供了七种格局来进展表单测试。typeselectcheckattach,和
press
方法允许你与拥有的表单输入举行交互。比如,让我们来设想一下一个留存于注册页面中的表单:

<form action="/register" method="POST">
  {{ csrf_field() }}
  <div>
    Name: <input type="text" name="name">
  </div>
  <div><input type="checkbox" value="yes" name="terms">Accept Terms</input></div>

  <div><input type="submit"></div>
</form>

咱俩可以编写一个测试来形成表单并检讨结果:

public function testNewUserRegistration()
{
  $this->visit('/register')
       ->type('Taylor', 'name')
       ->check('terms')
       ->press('Register')
       ->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')
       ->type('File Name', 'name')
       ->attach($absolutePathToFile, 'photo')
       ->press('Upload')
       ->see('Upload Successful!');
}

测试 JSON APIs

Laravel 同样也对测试 JSON APIs
和它们的响应提供了二种增援方法。比如,getpostputpatch,和
delete 方法可以用来公布一个对应 HTTP
行为方式的请求。你可以轻松的传递数据和头消息到这几个方式中。在开班之前,让我们来编排一个
POST 请求的测试来断言 /user 会重回给定数组的 JSON 格式:

<?php

class ExampleTest extends TestCase
{
  /**
   * A basic functional test example.
   *
   * @return void
   */
   public function testBasicExample()
   {
     $this->json('POST', '/user', ['name' => 'Sally'])
          ->seeJson([
            'created' => true,
          ]) ;
   }
}

seeJson 方法会转换给定的数组为 JSON,并且会申明应用响应的全部 JSON
中是否会并发相应的有的。所以,假设响应中还蕴涵其他 JSON
属性,那么这一个测试如故会被通过。

表达完全匹配的 JSON

要是你期望验证完整的 JSON 响应,你能够利用 seeJsonEquals 方法,除非
JSON 相应于所给定的数组完全合营,否则该测试不会被通过:

<?php

class ExampleTest extends TestCase
{
  /**
   * A basic functional test example.
   *
   * @return void
   */
   public function testBasicExample()
   {
     $this->json('POST', '/user', ['name' => 'Sally'])
          ->seeJsonEquals([
            'created' => true,
          ]);
   }
}

讲明匹配 JSON 结构

表明 JSON 响应是或不是拔取给定的布局也是足以的。你可以运用
seeJsonStructure 方法并传递嵌套的键列表:

<?php

class ExampleTest extends TestCase
{
  /**
   * A basic functional test example.
   *
   * @return void
   */
   public function testBasicExample()
   {
     $this->get('/user/1')
          ->seeJsonStructure([
            'name',
            'pet' => [
              'name', 'age'
            ]
          ]);
   }
}

在地点的例证中标明了盼望获取一个富含 namepet 属性的 JSON,并且
pet 键是一个涵盖 nameage
属性的目的。假使含有额外的键,seeJsonStructure
方法并不会破产。比如,如若 pet 还含有 weight
属性,那么测试依旧会被通过。

您可以运用 * 来断言所再次回到的 JSON
结构中的每一项都应有包涵所列出的那个属性:

<?php

class ExampleTest extends TestCase
{
  /**
   * A basic functional test example.
   *
   * @return void
   */
   public function testBasicExample()
   {
     // Assert that each user in the list has at least an id, name and email attribute.
     $this->get('/users')
          ->seeJsonStructure([
            '*' => [
              'id', 'name', 'email'
            ]
          ]);
   }
}

你也足以嵌套使用 *,下边的例证中,我们断言 JSON
响应再次来到的每一个用户都应该包蕴所列出的质量,并且 pet
属性应该也蕴藏所给定的特性:

$this->get('/users')
     ->seeJsonStructure([
       '*' => [
         'id', 'name', 'email', 'pets' => [
           '*' => [
             'name', 'age'
           ]
         ]
       ]
     ]);

会话 / 认证

Laravel 为测试时期的对话提供了四种相助方法。首先,你必要经过
withSession 方法来依据给定的数组设置 session
数据,那日常用来在测试应用的伸手到在事先先安装有些 session 数据:

<?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, 'backend')

剥夺中间件

当测试应用时,你可以方便的在测试中禁中间件。这使你可以凝集的测试路由和控制器而免除中间件的担心。你可以简简单单的引入
WithoutMiddleware trait 来在测试类中禁用所有的中间件:

<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class ExampleTest extends TestCase
{
  use WithoutMiddleware;

  //
}

只要您愿意只在分级的测试方法中禁用中间件,你可以在点子中应用
withoutMiddleware 方法:

<?php

class ExampleTest extends TestCase
{
  /**
   * A basic functional test example.
   *
   * @return void
   */
   public funcion 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());
}

假定你构造 POSTPUT,或者 PATCH
请求,你可以传递一个数组来作为请求的输入数据。当然,这几个数量可以通过
Request 实例来在您的路由和控制器中可用:

$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 = []); 断言客户端是否重定向到给定的动作
->assertSessionHas($key, $value = null); 断言会话中是否含有给定的键
->assertSessionHasAll(array $bindings); 断言会话中是否包含给定列表的数据
->assertSessionHasErrors($bindings = [], $format = null); 断言会话中是否包含错误数据
->assertHasOldInput(); 断言会话中是否包含旧的输入
->assertSessionMissing($key); 断言会话中是否缺失给定的键

与数据库合营

Laravel
也提供了种种立见成效的工具来使测试基于数据库驱动的运用更简便。首先,你可以动用
seeInDatabase
方法来断言给定的正经是还是不是能在数据库中非凡到相应的多少。比如,假设您愿意验证
users 表中是还是不是含有 emailsally@example.com
的记录,那么您可以这么做:

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

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

当然,类似 seeInDatabase
等格局唯有是为着测试的便利。你可以轻易的拔取任意的 PHPUnit
自带的断言方法来填补你的测试。

在种种测试后重置数据库

我们家常便饭要在历次测试后回复数据库,以便从前的测试数据不会对接着的测试爆发苦恼。

行使迁移

一种接纳是在下个测试此前举办数据库回滚和迁移操作。Laravel 提供了凝练的
DatabaseMigrations trait
来机关的处理这个,你只须求在测试类引入该性状:

<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class ExampleTest extends TestCase
{
  use DatabaseMigrations;

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

动用工作

其它一种选取就是在各类测试用例中都利用数据库的作业进行打包,这三次,Laravel
提供了有利于的 DatabaseTransactions trait 来机关的拍卖那些:

<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class ExampleTest extends TestCase
{
  use DatabaseTransactions;

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

注意: 这一表征只会卷入默许数据库连接的业务。

模型工厂

当测试时,日常须求在进行测试从前在数据库中添加一些测试所急需的数码记录。Laravel
允许你使用 “factories” 来对你的 Eloquent
模型定义一些默许的性质设置来代表这个手动的拉长数据的一言一动。在早先从前,大家来看一下接纳的
database/factories/ModelFactory.php
文件。该公文中只包涵了一个工厂的定义:

$factory->define(App\User::class, function (Faker\Generator $faker) {
  return [
    'name' => $faker->name,
    'email' => $faker->email,
    'password' => bcrypt(str_random(10)),
    'remember_token' => str_random(10),
  ] ;
});

在工厂定义中的闭包中,你可以回去一些模型中的用作测试的默许值。这几个闭包会接收一个
Faker
PHP 类库的实例,它会帮你生成一些即兴数据用于测试。

本来,你可以随意的在 ModelFactory.php
文件中添加额外的工厂。你也足以创建额外的厂子文件来有效的社团管制。比如,你可以在
database/factories 目录下制造一个 UserFactory.php 和 一个
CommentFactory.php 文件。

多少个工厂类型

偶尔,你可能希望在同一个 Eloquent
模型上有五个厂子类型。比如,可能您期望除了普通用户之外还有一个管理员的工厂。你可以利用
defineAs 方法来定义那么些工厂:

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

你可以动用 raw
方法来寻觅基础工厂的习性,那样可用以属性的复用,然后合并添加你所急需的额外属性:

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

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

在测试中行使工厂

假如您定义了工厂,你就能够在你的测试文件或者数据库 seed
文件中应用全局援救函数 factory
工厂方法来变化模型的实例。那么,让我们来看有的开立模型的言传身教。首先,大家将应用
make 方法,它将会创建一些模子可是却不会将其保存到数据库:

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

  // Use model in tests...
}

你可以通过传递一个数组到 make
方法中来掩盖模型中的默许值。只有指定的部分会被替换掉,而其他一些都会被封存为工厂定义的默许值:

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

你也得以依照给定的品类创设八个模型的集结:

// Create three App\User instances...
$user = factory(App\User::class, 3)->make();

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

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

厂子模型持久化

create 方法不仅会创建一个模子实例,它还会采纳 Eloquent 的 save
方法将其储存到数据库中:

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

  // Use model in tests...
}

那四回,你也可以传递一个数组到 create 方法中来覆盖默许的属性值:

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

为模型加上关系

你甚至足以持久化多少个模型到数据库中。在这一个事例中,大家竟然会为开创的模子附加一个涉及模型。当使用
create
方法来成立五个模型时,会回来一个汇聚实例,那允许你使用集合实例的不二法门,比如
each :

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

关联 & 属性闭包

您也可以在工厂定义内使用一个闭包来附加关系模型,比如,假诺你指望创造一个
Post 时也开创一个关联的 User 实例,你可以这么做:

$factory->fefine(App\Post::class, function($faker) {
  return [
    'title' => $faker->title,
    'content' => $faker->paragraph,
    'user_id' => function () {
      return factory(App\User::class)->create()->id;
    }
  ];
});

那些闭包还会收到工厂所涵盖的评估属性数组:

$factory->define(App\Post::class, function ($faker) {
  return [
    'title' => $faker->title,
    'content' => $faker->paragraph,
    'user_id' => function () {
      return factory(App\User::class)->create()->id;
    },
    'user_type' => function (array $post) {
      return App\User::find($post['user_id'])->type;
    }
  ]; 
});

模拟

如法炮制事件

假定你在 Laravel
中大批量选择了事件系统,那么你也许会想在进展测试时不接触事件照旧模拟运行一些事变。比如,如果你测试用户注册,你可能并不想触发所有
UserRegistered 事件,因为这么些可能会发送电子邮件等。

Laravel 提供了便利的 expectsEvents
方法来验证预期的轩然大波是或不是接触,不过会防止这几个事件的真正的施行:

<?php

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

    // Test user registration...
  }
}

你也能够利用 doesntExpectEvents 方法来申明给定的事件没有被触发:

<?php

class ExampleTest extends TestCase
{
  public function testPodcastPurchase()
  {
    $this->expectsEvents(App\Events\PodcastWasPurchased::class);

    $this->doesntExpectEvents(App\Events\PaymentWasDeclined::class);

    // Test purchasing podcast...
  }
}

比方您需要防止所有的事件触发,你可以应用 withoutEvents 方法:

<?php

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

    // Test user registration code...
  }
}

仿照任务

偶然,你想在营造请求到应用时,不难的测试一下决定器中是或不是举办了点名职分的散发。那足以使您隔离测试你的路由
/
控制器和你的天职逻辑,当然,你可以再写一个测试用例来单独的测试义务的执行。

Laravel 提供了有益的 expectsJobs
方法来表明期望的天职是或不是被分发,但是职分并不会被真正的分发执行:

<?php

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

    // Test purchase podcast code...
  }
}

只顾:那些方式仅用来测试通过 DispatchesJobs trait 的分发或者
dispatch 支持方法的散发。它并不检测一向行使 Queue::push
发表的职务。

效仿假面

当测试时,你可能日常须要效法一个 Laravel
假面的调用。比如,考虑一下上边的控制器操作:

<?php

namespace App\Http\Controllers;

use Cache;

class UserController extends Controller
{
  /**
   * Show a list of all users of the application
   *
   * @return Response
   */
   public function index()
   {
     $value = Cache::get('key');

     //
   }
}

大家得以行使 shouldReceive 方法来模拟调用 Cache 假面,它会回来一个
Mockery
的萧规曹随实例。由于假面实际上是通过 Laravel
的劳务容器来分析和管理的,所以它们比相似的静态类更具可测性。比如,让我们来模拟
Cache 假面的调用:

<?php

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

    $this->visit('/users')->see('value');
  }
}

在意:你不应有模拟 Request 假面。你应当在运行测试时选用 HTTP
辅助方法如 callpost 来传递您所梦想的输入。

相关文章