Laravel Testing - fantasy0107/notes GitHub Wiki

目錄tests中分成Feature和Unit
Unit:small portion of your code(single method)
Feature:large portion of your code

phpunit.xml定義環境變數

概念

  • 測試最小單位的邏輯,通常是 Class 的 method
  • 不依賴外部「不可控」的環境
  • 測試裡不具備邏輯:如 if else, switch case …
  • 測試案例之間彼此獨立
  • 一次只測試一件事情

單元測試的 FIRST 原則

  • Fast:快速。可以很快看到結果。
  • Independent:獨立。測試案例之間不互相影響。
  • Repeatable:可重複。可重複執行而不影響預期結果。
  • Self-Validating:不需要人工介入驗證。
  • Timely:及時。程式完成即可立刻驗證。

單元測試的 3A 原則

  • Arrange : 初始化目標物件、相依物件、方法參數、預期結果,或是預期與相依物件的互動方式。
  • Assume:預期結果、或預期的互動
  • Act : 呼叫目標物件的方法。
  • Assert : 驗證是否符合預期。

貼上 web phpinfo() 的資訊

文章

開發時

  1. 所有的單元測試必須要正確
  2. code 符合它的設計準則
  3. code 沒有重複
  4. code 只有最少數量的 class 和 method

除錯時

  1. 檢驗你重複出現的錯誤
  2. 找出 code 中最小的錯誤
  3. 寫出自動測試是會錯誤但是只要 code 修正就會成功通過
  4. 修正錯誤
  • --debug 詳細列出跑了什麼測試
  • --coverage-html 產出 code coverage 報表用 html 的形式
  • --filter 要跑怎麼樣的測試 method name

Fixtures (基礎設施)

public function setUp()
{
	parent::setUp();
   	//初始化測試時做的事情在每個方法執行之前    
}

public function tearDown()
{
	parent::tearDown();
	//消滅測試時做的事情在每個方法執行之後
}

assert

annotation

  • @expectedException ExceptionName - 預計丟出Exception
    如果是框架自行提供的Exception \Illuminate\Database\Eloquent\ModelNotFoundException

建立test檔案

檔案名稱和要測的檔案名稱一樣+Test
只是在method名稱的部份則是test + methodName + 其它

//artisan
php artisan make:test UserTest --unit

跑測試

//平常
./vendor/bin/phpunit ....

//有架docker 要跑進 docker 下
docker exec -it php7.1 bash 
./vendor/bin/phpunit ....

https://phpunit.de/manual/6.4/en/textui.html#textui.clioptions

目錄架構

Tests/Unit目錄底下就測試service/repository/helper
目錄架構就按照app底下的

//example

/**
 * App底下
 */
app\service\UserService

/**
 * Tests底下
 */
Tests\Unit\Service\UserService 

Repository

public function setUp()
{
        parent::setUp();  
        $this->initDatabase();  
        $this->userRepository = new UserRepository();  
}

public function tearDown()
{
    $this->resetDatabase();
    $this->userRepository = null;
    parent::tearDown();
}

使用factory

services

mock出__construct需要的的class後丟進去
class 是namespace+classname

$userService = new UserService($A, $B, $C, $D);

指令

vendor\bin\phpunit Tests\Unit\Services\UserServiceTest.php
//範例
public function testAdd()
{
    $userRepository = $this->createMock('App\TasklistPlatform\Repositories\UserRepository');
    $taskRepository = $this->createMock('App\TasklistPlatform\Repositories\TaskRepository');

    $userService = new UserService($userRepository, $taskRepository);
    $this->assertEquals(2, $userService->add(1,2));      
}

文件

PHPUnit
Laravel Seeding
Laravel Factory

--debug // 除錯用
--filter=functionName //測試特定的 test case 

Laravel factory

factory()->make(); // 產生假資料而且不會新增到資料庫 資料格式會是你 factory 檔案設定的樣子
factory()->create(); // 產生假資料而且會新增到資料庫

產生假資料

use Faker\Generator as Faker;

範例

Get

/**
 * 顯示使用者資訊和相關回傳結構 - 成功
 */
 public function testShowUserOKWithStatusAndAttributes()
    {
        $response = $this->withHeaders($this->authorization)
                         ->json('GET', 'api/v1/me');

        $response->assertStatus(200)
                 ->assertJsonStructure([
                    'result',
                    'data' => [
                        'thumbnail',
                        'levelName',
                        'percentOfThisLevel',
                        'yCoin',
                        'diamonds'
                    ]
                 ]);
    }

Find

$rule = factory(Rule::class)->create();
        
$response = $this->withHeaders([
    'Authorization' => $this->authorization,
])
->get('/api/rules/'.$rule->id);


$response->assertStatus(200)
         ->assertJsonStructure([
                    'id',
                    'user_id',
                    'title',
                    'created_at',
                    'updated_at',
                    'deleted_at'
]);

Create

$user = User::first();

        $response = $this->withHeaders([
            'Authorization' => $this->authorization,
        ])
        ->post('/api/users/'.$user->id.'/rules', [
            'title' => 'PHPUnit'
        ]);

        $response->assertStatus(201)
                ->assertJsonStructure([
                    'id',
                    'user_id',
                    'title',
                    'created_at',
                    'updated_at'
                ]);

Update

 $rule = Rule::first();

        $response = $this->withHeaders([
            'Authorization' => $this->authorization,
        ])
        ->json('PUT', '/api/users/'.$rule->user_id.'/rules/'.$rule->id, [
            'title' => 'PHPUnit putA'
        ]);

        $response->assertStatus(200);

Delete

$rule = factory(Rule::class)->create();

$response = $this->withHeaders([
    'Authorization' => $this->authorization,
])
->json('DELETE', '/api/users/'.$rule->user_id.'/rules/'.$rule->id);

$response->assertStatus(204);

寫出有價值的測試

原本的觀念

  1. Every dependency should be mocked and tests should never touch the database!

作者的觀念 for Laravel developers

  1. Start testing the entire request / response cycle for a given feature without mocks
public function test_action_events_are_not_created_if_the_action_fails()
{
    $user = factory(User::class)->create();

    $response = $this->postJson('/api/users/action?action=FailingAction', [
        'resources' => [$user->id],
    ]);

    $response->assertStatus(500);
    $this->assertCount(0, ActionEvent::all());
}
⚠️ **GitHub.com Fallback** ⚠️