Background

隨著工作越來越多年(年紀越來越大?), 深深的覺得測試是一件很重要的事情. 所以在新開發的應用中都會蠻注重 unitTest 的部分, 那最近在學 Laravel 所以就記錄一下怎麼在 Laravel中使用 unitTest.

Creating a New Test

$ php artisan --version
Laravel Framework 5.4.22

一開始先確定一下自己使用Laravel的版本. 在5.4的版本中 unitTest 的相關程式都放在 /tests 資料夾中. 以下是資料夾中的檔案:

  • CreatesApplication.php
  • Feature
  • TestCase.php
  • Unit

那基本的 unitTest 的 code 應該都是放在 Feature 的資料夾下面. 那記得有命名的規則(要以Test.php結尾):

{name}Test.php, This format allows PHPUnit to find each test class — it will ignore anything that does not end in Test.php

首先利用 Artisan 來創造一個簡單的 test

$ php artisan make:test BasicTest

創完之後整個程式會像這樣:

<?php

namespace Tests\Feature;

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

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

那在這邊需注意的是 test prefix on the method. PHPUnit 會根據這個 test prefix 來決定這個 method 需不需要被執行. 如果 method 名稱不是以 test開頭的話則會被忽略掉.

phpunit.xml

phpunit.xml 這個檔案在主目錄下.

...
    <testsuites>
        <testsuite name="Feature Tests">
            <directory suffix="Test.php">./tests/Feature</directory>
        </testsuite>

        <testsuite name="Unit Tests">
            <directory suffix="Test.php">./tests/Unit</directory>
        </testsuite>
    </testsuites>
...

在這個檔案中我們可以知道 PHPUnit 會執行 feature & unit 這兩個資料夾下的 test 檔案.

You can run your PHPUnit tests by running the phpunit command:

./vendor/bin/phpunit

執行後應該會出現下面的訊息

PHPUnit 4.8.35 by Sebastian Bergmann and contributors.

...

Time: 273 ms, Memory: 14.75MB

OK (3 tests, 3 assertions)

P.S. 但是我是把原本PHPUnit從5.7.x to 4.8.35 才能執行.

PHPUnit 在 Oct.02,2015 宣布重要改版, PHPUnit 5.x 將支援PHP 5.6與PHP 7,PHPUnit 6.x將只支援PHP 7, 而PHP 5.3、PHP 5.4與PHP 5.5將只能使用 PHPUnit 4.x.

Writing a Basic Test

首先在

/app/Http/Controllers 建立一個新檔案

/app/Http/Controllers/Box.php

<?php
namespace App\Http\Controllers;

class Box extends Controller
{
/**
* @var array
*/
    protected $items = [];

/**
* Construct the box with the given items.
*
* @param array $items
*/
    public function __construct($items = [])
    {
        $this->items = $items;
    }

/**
* Check if the specified item is in the box.
*
* @param string $item
* @return bool
*/
    public function has($item)
    {
        return in_array($item, $this->items);
    }

/**
* Remove an item from the box, or null if the box is empty.
*
* @return string
*/
    public function takeOne()
    {
        return array_shift($this->items);
    }
/**
* Retrieve all items from the box that start with the specified letter.
*
* @param string $letter
* @return array
*/
    public function startsWith($letter)
    {
        return array_filter($this->items, function ($item) use ($letter) {
            return stripos($item, $letter) === 0;
        });
    }
}

接著在 tests/Feature 建立一個新檔案

tests/Feature/BoxTest.php

<?php

namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use App\Http\Controllers\Box;

class BoxTest extends TestCase
{
    public function testHasItemInBox()
    {
        $box = new Box(['cat', 'toy', 'torch']);

        $this->assertFalse($box->has('toy'));
        $this->assertFalse($box->has('ball'));
    }
}

完成後就執行 Test 指令, 會出現以下訊息. 你會發現有一個測試沒過.

$ ./vendor/bin/phpunit
PHPUnit 4.8.35 by Sebastian Bergmann and contributors.

F..

Time: 1.31 seconds, Memory: 14.25MB

There was 1 failure:

1) Tests\Feature\BoxTest::testHasItemInBox
Failed asserting that true is false.

tests/Feature/BoxTest.php:17

FAILURES!
Tests: 3, Assertions: 3, Failures: 1.

基本上大致上是這樣在運作. 那接著我們簡單講一下有哪些 assert 的用法.

  • assertTrue()
  • 判斷是不是 True
  • assertFalse()
  • 判斷是不是 False
  • assertEquals()
  • assertEquals() is used to compare the actual value of the variable to the expected value.
  • assertNull()
  • 判斷是不是 Null
  • assertContains()
  • assertContains() asserts that an expected value exists within the provided array
  • assertCount()
  • assertCount() asserts the number of items in the array matches the specified amount
  • assertEmpty()
  • assertEmpty() asserts that the provided array is empty

Testing JSON APIs

ok. 現在的 web 基本上用 JSON 來傳遞資料已經變一個主流的方式了. 所以Laravel也有提供相對應的服務來讓你測試 JSON 的內容, 來看一個官網的例子

<?php

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function testBasicExample()
    {
        # 模擬一個 Http request 來得到 response
        $response = $this->json('POST', '/user', ['name' => 'Sally']);

        # 接著對這個 response 來做驗證
        $response
            ->assertStatus(200)
            ->assertExactJson([
                'created' => true,
            ]);
    }
}

Reference