Search code examples
laravellaravel-testing

General error: 1 no such table: App\Models\ModelName while testing laravel


My test:

class FloorStackTest extends TestCase
{
    use RefreshDatabase, WithFaker, DatabaseMigrations;
    protected $endPoint = '/endpoint';

    public function test_unit_type_note_added_successfully()
    {
        $this->signIn();
        $this->withoutExceptionHandling();
        $unitType = UnitType::factory()->create();
        $note = $this->faker()->sentence(3);
        $resp = $this->ajaxPost($this->admin_route.$this->endPoint."/".$unitType->property_version_id."/save-ut-note",[
            'notes' => [
                [
                    "ut_note_id" => "utn_".$unitType->id,
                    "note" => $note
                ]
            ]
        ])->assertStatus(Response::HTTP_OK);
        $results = [
            'notes'         => $note
        ];


        //i could print upto here
        dd('hit');

        $this->assertDatabaseHas(UnitType::class,$results);

        //but could not print here
        dd('hit');
        
    }
}

I am using sqlite for testing and I am using laravel 8 (which was previously updated from laravel 5, just in case to confirm)

I the above code, you can see the point upto where I could print hit.

I am using .env.testing

DB_CONNECTION=sqlite
DB_DATABASE="database/test.sqlite"
DB_FOREIGN_KEYS=false

I have already been using: use RefreshDatabase but still it is showing error. and the full error is:

Tests\Feature\FloorStackTest::test_unit_type_note_added_successfully
Illuminate\Database\QueryException: SQLSTATE[HY000]: General error: 1 no such table: App\Models\UnitType (SQL: select count(*) as aggregate from "App\Models\UnitType" where ("notes" = Eveniet incidunt consequuntur dolore est.))

And my model UnitType has this

    use SoftDeletes, HasFactory;
    protected $table = 'unit_types';

    protected $guarded = ['id'];
    protected $dates = ['created_at','updated_at','deleted_at'];

Solution

  • First, note that passing a Model, or fully qualified class name, to the assertDatabaseHas method is only available starting in version 8 of the framework. See the signature change below:

    Signature in v7: protected $this assertDatabaseHas(string $table, array $data, string|null $connection = null).

    Signature in v8: protected $this assertDatabaseHas(Model|string $table, array $data, string|null $connection = null).

    Ultimately, the assertion has to be made using the actual table name. This is how it is demonstrated in the docs:

    $this->assertDatabaseHas('users', [
        'email' => '[email protected]',
    ]);
    

    So if you're using Laravel <8, you could to pass the table name string to get the desired results:

    $this->assertDatabaseHas('unit_types', $results);
    

    As an alternative, taken from this answer, you could still use the model class to get the table name instead of hard-coding the string.

    Something like this:

    $this->assertDatabaseHas((new UnitType)->getTable(), $results);
    

    In Laravel 8, the method will take a model, and run it through this method to get the table name:

    protected function getTable($table)
    {
        return is_subclass_of($table, Model::class) ? (new $table)->getTable() : $table;
    }
    

    Here table can be a model instance, or the fully qualified class name (new UnitType, or UnitType::class). Just note that it has to extend the base Model class in order for Laravel to parse it correctly, otherwise it will just pass along the class name string to the query.