解決在 Laravel 使用 PHPUnit + SQLite 的 RefreshDatabase 失敗問題

建立於 5/10/2024 • 更新於 5/10/2024

之前在做單元測試需要用到 RefreshDatabase 的時候,專案內部分的 migration 會讓 SQLite 噴錯無法順利執行,以下紀錄解決方法:

1. SQLite 無法使用 drop foreign key

database/migrations/xxx.php:

Schema::table('users', function(Blueprint $table) {
    // ...
    $table->dropForeign(['role_id']);
});

錯誤訊息:

BadMethodCallException: SQLite doesn't support dropping foreign keys (you would need to re-create the table).

解法參考: GitHub Laravel Issue

tests/TestCase.php:

<?php

namespace Tests;

use Closure;
use Illuminate\Database\Connection;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\SQLiteBuilder;
use Illuminate\Database\SQLiteConnection;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication;
    use RefreshDatabase;

    public function __construct()
    {
        parent::__construct();
        $this->withoutForeign();
    }

    private function withoutForeign()
    {
        Connection::resolverFor('sqlite', function ($connection, $database, $prefix, $config) {
            return new class($connection, $database, $prefix, $config) extends SQLiteConnection
            {
                public function getSchemaBuilder()
                {
                    if ($this->schemaGrammar === null) {
                        $this->useDefaultSchemaGrammar();
                    }
                    return new class($this) extends SQLiteBuilder
                    {
                        protected function createBlueprint($table, Closure $callback = null)
                        {
                            return new class($table, $callback) extends Blueprint
                            {
                                public function dropForeign($index)
                                {
                                }
                            };
                        }
                    };
                }
            };
        });
    }
}

2. 在 SQLite 顯示無法 dropColumn

database/migrations/xxx.php:

Schema::table('users', function(Blueprint $table) {
    // ...
    $table->dropColumn('description');
    $table->dropColumn('deleted_at');
});

錯誤訊息:

BadMethodCallException: SQLite doesn't support multiple calls to dropColumn / renameColumn in a single modification.

解法: 將 migration 改為如下:

Schema::table('users', function(Blueprint $table) {
    // ...
    $table->dropColumn(['description', 'deleted_at']);
});
My Avatar

Chris Wang

Keep Learning, and Keep Coding.

草木園丁兼全端工程師 • 期許自己每天多懂一點新知識 • 莫欺少年窮