StanleyPeters's avatar

Preventing accidental refresh on test/stage db

After an embarrassing number of accidental refreshes of the test db during testing I was inspired to this solution from https://stackoverflow.com/questions/64096031/how-to-disable-laravel-php-unit-test-in-production. TestCase.php

    protected function setUp(): void
    {
        parent::setUp();
        if (env('DB_HOST') !== 'local_db') {
            throw new \Exception( "DB_HOST Must use Local DB for testing!");
        }
    }

Or for multi schema testing

    protected function setUp(): void
    {
        parent::setUp();
        collect(['DB_HOST', 'DB2_HOST', 'DB3_HOST'])->each(function ($db) {
            if (env($db) !== 'local_db') {
                throw new \Exception( "$db Must use Local DB for testing!");
            }
        });
    }

Requires Docker container named local_db and DB_HOST, DB2_HOST, DB3_HOST defined as local_db in env.testing.

Hope this helps somebody. Maybe even me.

0 likes
3 replies
tisuchi's avatar

@stanleypeters Just a quick opinion.

Why do we need to predefined data in db for testing?

The main idea for testing to define test environment (in the arrange/given section) for every single test. Supporting this statement, to me, if we follow AAA or Given-when-then approach, we should define the arrange section for every test, no matter what.

For example:

use RefreshDatabase; // This will ensure your database remains untouched after tests

/** @test */
    public function a_post_can_be_created()
    {
        // Arrange
        $postData = [
            'title' => 'Sample Post',
            'content' => 'This is the content of the sample post.'
        ];

        // Act
        $post = Post::create($postData);

        // Assert
        $this->assertDatabaseHas('posts', $postData);
        $this->assertEquals($postData['title'], $post->title);
        $this->assertEquals($postData['content'], $post->content);
    }

ℹ️ Arguably, I might be wrong. But this is my understanding about test.

Tray2's avatar

@tisuchi yes, that is basically what it is about, and this is what it can look like in real life using Pest.

it('stores a valid movie', function () {
    actingAs($this->user)->post(route('movies.store', $this->validMovie))
        ->assertRedirect(route('movies.index'));
    assertDatabaseCount('movies', 1);
    assertDatabaseCount('actor_movie', 1);
});

it('stores a valid movie with multiple actors', function () {
    $actor = Actor::factory()->create();
    $validMovie = $this->validMovie;
    $validMovie['actor'] = [
        "{$this->actor->first_name} {$this->actor->last_name}",
        "{$actor->first_name} {$actor->last_name}",
    ];

    actingAs($this->user)->post(route('movies.store', $validMovie))
        ->assertRedirect(route('movies.index'));
    assertDatabaseCount('movies', 1);
    assertDatabaseCount('actor_movie', 2);
});
1 like

Please or to participate in this conversation.