thebigk's avatar
Level 13

phpUnit is deleting my database!

Okay, I hope that this isn't a bug. This is my test case

<?php

namespace Tests\Feature\Discussions;

use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;


class ThreadsTest extends TestCase
{
    use RefreshDatabase;

    /** @test */
    public function guests_can_view_thread_list()
    {
        $thread = factory('App\Model\Discussions\Thread')->create(['state' => 1]);
        $this->get('/threads/latest')
            ->assertSee($thread->title);
    }

}

My phpUnit.xml has :

<php>
        <env name="APP_ENV" value="testing"/>
        <env name="DB_CONNECTION" value="sqlite"/>
        <env name="DB_DATABASE" value=":memory:"/>
        <env name="CACHE_DRIVER" value="array"/>
        <env name="SESSION_DRIVER" value="array"/>
        <env name="QUEUE_DRIVER" value="sync"/>
    </php>

...Which is the standard set up I've been using for my trial projects. Today, I upgraded composer and brew; then did a fresh install of Laravel and wrote my first test. This simple test produced erratic behavior - it'd pass or fail randomly on each run.

I then decided to find out the pattern; and ran the phpUnit (first through terminal, then through phpStorm) and was surprised to find out that the test actually deleted my database!

My prime suspect is that the RefreshDatabase thing is actually working on my real database and not sqlite.

I was able to reproduce this issue 3 times at the time of creating this thread. However, I did another composer dumpautoload and php artisan cache:clear and now, I'm getting random pass or fails on this test case.

Can someone point me in the right direction? I'm unable to figure out what's going on.

0 likes
23 replies
JhumanJ's avatar

Could you show us your config/database.php ?

tykus's avatar

Are you using a global phpunit? Have you tried running the local version to see if there is something different?

$ ./vendor/bin/phpunit
thebigk's avatar
Level 13

@JhumanJ - Haven't touched the config/database.php yet, it's straight out of Laravel vanilla install.

@tykus - I've never had such problem before and have no clue about local/global phpUnit. All I changed was -

  1. phpUnit.xml to add DB_CONNECTION=sqlite and DB_DATABASE=:memory:
  2. Created a test case, as I'd normally do and added use Illuminate\Foundation\Testing\RefreshDatabase;

The simple test case above would give me pass/fail on different runs, without changing anything in the model or controller.

PS: I do not have access to my main dev machine right now. I'll try to reproduce the issue on my laptop and report back.

tykus's avatar

In the top of your web routes file, dump the environment.

dd(App::environment());

If the result comes back local or production then your phpunit.xml config is being ignored/overwritten. This might be because config has been cached; try php artisan cache:clear and php artisan config:clear commands.

4 likes
thebigk's avatar
Level 13

@tykus - Yes, after clearing the cache, database didn't delete; but the test case would continue to produce erratic result. I'm currently reproducing the setup on my laptop. Will get back in some time with my findings.

thebigk's avatar
Level 13

Update:

So, I tried creating the environment on my second machine. While recreating it, I found one possible bug that could have caused the issue.

In my thread factory, I had 'user_id' set to be generated using a random number between 1-100. Now, since sqlite had no idea about the existing users, it might have produced random results.

However, I'd expect phpUnit to report 'fail'. But it didn't.

Can there be an event where Laravel will ignore then phpUnit.xml and go back to regular database (and then Refresh it? )

tykus's avatar

Are you on a case-sensitive OS? You keep writing phpUnit.xml, whereas I would expect this filename to be phpunit.xml - might be nothing, or it might be something!

1 like
antoniocusro's avatar

I have the same problem with a new Laravel 5.5 project. I have the phpunit.xml setup as listed here.

The only thing I noticed is that when I run the tests via "phpunit" from the terminal, everything runs ok, but when I run the tests via PhpStorm, it deletes the database, so I guess that in some specific scenario the test environment from phpunit.xml is not loaded.

--- Edit ---

I found the issue and the solution. For running the tests from PhpStorm directly, you need to tell PhpStorm the path to your test configuration file (phpunit.xml). You will find this setting in: Settings > Languages & Frameworks > PHP > Test Frameworks. Then look for: "Default configuration file". Point this to your project phpunit.xml.

4 likes
kyslik's avatar

I had this happen to me as well, If you run tests from PHPStorm set preferences to use the XML file in root directory.

3 likes
arukomp's avatar

I really think this is an environment file issue. I got burned this way a couple of times. The problem was that instead of .env.testing phpunit was using .env which had my development database.

Just double check that you have a .env.testing file and make sure your phpunit.xml is set up properly to use the testing environment

3 likes
lucasmmduran's avatar

I had the same problem. In my especific case, was database.php

I configured in this way and worked:

    'sqlite' => [
        'driver' => 'sqlite',
        'database' => env('DB_DATABASE', database_path('database.sqlite')),
        'prefix' => '',
    ],

and phpunit.xml:

    <env name="APP_ENV" value="testing"/>
    <env name="DB_CONNECTION" value="sqlite"/>
    <env name="DB_DATABASE" value=":memory:"/>
    <env name="BCRYPT_ROUNDS" value="4"/>
    <env name="CACHE_DRIVER" value="array"/>
    <env name="SESSION_DRIVER" value="array"/>
    <env name="QUEUE_DRIVER" value="sync"/>
    <env name="MAIL_DRIVER" value="array"/>
1 like
kevin73911's avatar

In phpstorm you should let laravel's phpunit.xml file to be the default configuration file. There is a youtube link for you to see how to setup.

https://www.youtube.com/watch?v=-6U1pKx9agE&t=72s

By the way, if you don't want to watch the whole video, just watch 2:57, there is a column called Default configuration file and just edit it and everything will be fine.

R3N's avatar

This is easily fixed by using php artisan cache:clear This was caused by using php artisan config:clear before.

3 likes
robjbrain's avatar

Just ran into this as well.

I'm confused by the posts in here and lack of a solution.

It absolutely seems to be caused by config:cache which results in the environment being "local" instead of "testing" but there must be some way around this? If config is cached by accident on local for any reason it seems crazy that the result is the immediate loss of the database with no warnings?

What is it about it config:cache that causes this? Is there a way to make phpunit not use the cache? That seems sensible, why would you ever want phpunit using a warm cache?

6 likes
Simon_Barrett's avatar

I fixed this by changing CreatesApplication.php to:

<?php

namespace Tests;

use Illuminate\Contracts\Console\Kernel;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Artisan;

trait CreatesApplication
{
    /**
     * Creates the application.
     *
     * @return Application
     */
    public function createApplication(): Application
    {
        $app = require __DIR__.'/../bootstrap/app.php';

        $app->make(Kernel::class)->bootstrap();

        $this->clearConfigCache();

        return $app;
    }

    private function clearConfigCache(): void
    {
        Artisan::call('config:clear');
    }

}
4 likes
dhikrullah's avatar

@Simon_Barrett Thank you, works like charm.

I just add this in my branch, and before I push to staging or main, I do ensure remove it after using it.

stanliwise's avatar

I think this is a laravel bug, anytime optimization is run on laravel, it uses the cached config settings and thereby overwrites the database.

1 like
Sinnbeck's avatar

@stanliwise As optimizations (caching) is only designed to be run on production and not on testing/dev, this should never happen..

But of course, you are free to send a PR to the laravel team to "fix" it :)

1 like
stalinko's avatar

@stanliwise completely agree! I've just realized I've accidentally dropped my local database with a loooot of testing data I've been collecting during several months :((( Now I have to waste an hour or so to restore it. And everything is because I simply ran Laravel tests. The only positive moment is that it didn't happen in production env. Now I'm afraid to run tests over there.

Sinnbeck's avatar

@stalinko You never run tests on production.. Thats what your test server is for.

2 likes
machi7's avatar

I spent 2 days debugging this and finally found the fix.

So I cloned a Laravel 10 project that already has initial feature tests from my colleagues. Everybody had no problem running the tests but me. Anyway, I'm also using Laragon in my local machine.

Thanks to this forum and several stackoverflow discussions, I was able to debug one by one what the problem is.

First, I discovered that phpunit.xml variables aren't being read. Turned out <env> should be changed to <server> if you are using Laravel 8 or later. I also added force="true" at the DB_CONNECTION and APP_ENV variables there. Ran the artisan test again and it spit out a different error. I thought the problem was that .env.testing wasn't being called. But one of the errors say something about sqlite driver not enabled, it turned out that sqlite extension wasn't available. Final fix was uncommenting sqlite extension in php.ini and laragon restart.

Hope this helps.

jeromejaglale's avatar

I removed the trait RefreshDatabase from my tests (it was not necessary) and it fixed that problem.

Please or to participate in this conversation.