Creating and dropping a table during test breaks RefreshDatabase trait

Published 1 week ago by flyingL123

I've created a trait using in my Laravel 5.6 app. The trait is called Projectable. This trait is meant to be used by Eloquent models. In order to test the trait, I wanted to create a ProjectableStub model to use in the tests. However, since this is an Eloquent model, it requires a table.

I wanted to simply create and drop a table just for testing. However, when I do this, it seems like something breaks regarding the RefreshDatabase functionality. To demonstrate, I am simply running two tests, both of which try to create a Product model with id = 1. Since the RefreshDatabase trait is being used, this should work fine. And, in the example below, it does:

    <?php
    
    namespace Tests\Feature;
    
    use App\Product;
    use Tests\TestCase;
    use App\Concerns\Projectable;
    use Illuminate\Support\Facades\Schema;
    use Illuminate\Foundation\Testing\WithFaker;
    use Illuminate\Foundation\Testing\RefreshDatabase;
    use Illuminate\Database\Eloquent\Model as Eloquent;
    
    class ProjectableTest extends TestCase
    {
        use RefreshDatabase;
    
        public function setUp()
        {
            parent::setUp();
    
            //$this->createStubTable();
        }
    
        /**
         * @test
         */
        public function example_first_test()
        {
            factory(Product::class)->create(['id' => 1]);
        }
    
        /**
         * @test
         */
        public function example_second_test()
        {
            factory(Product::class)->create(['id' => 1]);
        }
    
        public function tearDown()
        {
            //$this->dropStubTable();
    
            parent::tearDown();
        }
    
        private function createStubTable()
        {
            Schema::create('stubs', function ($table) {
                $table->increments('id');
                $table->string('name');
                $table->timestamps();
            });
        }
    
        private function dropStubTable()
        {
            Schema::dropIfExists('stubs');
        }
    }

    class ProjectableStub extends Eloquent
    {
        use Projectable;
    
        protected $table = 'stubs';
    
        protected $guarded = [];
    }

However, as soon as I uncomment the two lines so that the stubs table is created and dropped, I get a SQL error that a duplicate ID is being used:

    <?php
    
    namespace Tests\Feature;
    
    use App\Product;
    use Tests\TestCase;
    use App\Concerns\Projectable;
    use Illuminate\Support\Facades\Schema;
    use Illuminate\Foundation\Testing\WithFaker;
    use Illuminate\Foundation\Testing\RefreshDatabase;
    use Illuminate\Database\Eloquent\Model as Eloquent;
    
    class ProjectableTest extends TestCase
    {
        use RefreshDatabase;
    
        public function setUp()
        {
            parent::setUp();
    
            $this->createStubTable();
        }
    
        /**
         * @test
         */
        public function example_first_test()
        {
            factory(Product::class)->create(['id' => 1]);
        }
    
        /**
         * @test
         */
        public function example_second_test()
        {
            factory(Product::class)->create(['id' => 1]);
        }
    
        public function tearDown()
        {
            $this->dropStubTable();
    
            parent::tearDown();
        }
    
        private function createStubTable()
        {
            Schema::create('stubs', function ($table) {
                $table->increments('id');
                $table->string('name');
                $table->timestamps();
            });
        }
    
        private function dropStubTable()
        {
            Schema::dropIfExists('stubs');
        }
    }

    class ProjectableStub extends Eloquent
    {
        use Projectable;
    
        protected $table = 'stubs';
    
        protected $guarded = [];
    }
  1. Tests\Feature\ProjectableTest::example_second_test Illuminate\Database\QueryException: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '1' for key 'PRIMARY'

Does anyone know why creating and dropping a table within the test is causing this issue? Is there a better way to go about this? Maybe some way to add a migration at runtime for this new table?

flyingL123

Making the stubs table temporary did the trick. Creating a table causes an implicit commit in mysql. Creating a temporary table does not. It also means I don't need to drop the table either, since it happens automatically:

Schema::create('stubs', function ($table) {
    $table->temporary()
    $table->increments('id');
    $table->string('name');
    $table->timestamps();
});

Seems to be working perfectly so far.

Please sign in or create an account to participate in this conversation.