41

I have a .env file containing my database connection details, as is normal for Laravel 5. I want to override these for testing, which I can do in phpunit.xml. However, doing this seems to go against the philosophy of .env which is not to commit environmental configurations, particularly passwords.

Is it possible to have something like .env.testing and tell phpunit.xml to read from that?

miken32
  • 42,008
  • 16
  • 111
  • 154
Gnuffo1
  • 3,478
  • 11
  • 39
  • 53

10 Answers10

26

You could override the .env file being used in your TestCase file, where the framework is booted for testing.

More specific:

tests/TestCase.php

/**
 * Creates the application.
 *
 * @return \Illuminate\Foundation\Application
 */
public function createApplication()
{
    /* @var \Illuminate\Foundation\Application $app */
    $app = require __DIR__ . '/../bootstrap/app.php';

    $app->loadEnvironmentFrom('.env.testing'); // specify the file to use for environment, must be run before boostrap

    $app->make('Illuminate\Contracts\Console\Kernel')->bootstrap();

    return $app;
}

All the tests extending TestCase will use this configuration file.

Please note that any setting defined in phpunit.xml will override this configuration.

Update

Starting Laravel5.4, the createApplication function is no longer found in tests\TestCase. It has been moved to tests\CreatesApplication trait.

Bogdan
  • 5,368
  • 9
  • 43
  • 62
  • 2
    Your answer looked promising, but for some reason it's still loading .env. – Seán Hayes Sep 11 '15 at 17:53
  • @SeánHayes make sure you run `$app->loadEnvironmentFrom(..)` *before* boostrap() otherwise it will use the default .env – Bogdan Sep 14 '15 at 08:06
  • 1
    This kinda looks like a bad practice. – Dimitri Acosta Nov 04 '15 at 16:00
  • @DimitriAcosta, why do you think that? – Bogdan Nov 04 '15 at 16:29
  • 1
    Because you shouldn't be changing the code for your tests. – Dimitri Acosta Nov 04 '15 at 21:45
  • I don't get what you're saying. Would you like to elaborate? – Bogdan Nov 05 '15 at 07:54
  • 1
    This should be the correct answer. Doing so we do not have to change the production code. Thanks a lot @Bogdan! – rdok Jul 08 '16 at 07:45
  • 1
    @DimitriAcosta IMHO, this approach will also work, if you do not want *secret* environment variables added to version control. –  Oct 10 '17 at 08:35
  • 1
    @DimitriAcosta To a junior like yourself, yes, it does look like bad practice. To experienced developers, it's good. We can run our tests under multiple environments. This means we can use a env file for SQLite, and a separate one for MySQL, just as a small example. Your answer is spam, because it's not what was asked. – Sunhat Jan 24 '22 at 14:50
24

Copy your .env to .env.testing, then edit the .env.testing file and change the APP_ENV parameter to make it like this APP_ENV=testing this way you will be able to specify your settings int this new file

In case you don't want to create a new .env.testing file you have to specify your variables in the phpunit.xml in the php section with the values you need, something like this

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

Just use the key values in the name section and the value of that key in the value section.

For this example I am specifying phpunit to use an sqlite database with the name of testing.

By the way in config/database.php I added this 'default' => env('DB_CONNECTION', 'mysql'), to use mysql by default unless I specify something diferent, as in this case.

Dimitri Acosta
  • 1,756
  • 12
  • 15
  • 1
    I know you can do that in phpunit.xml, I said that in my question. What I want to avoid is specifying database connection details in there as then I'll be committing them. – Gnuffo1 Jul 29 '15 at 09:41
  • Whereas this is just for tests purposes it shouldn't matter if you place the values in the phpunit.xml, what I recomend is to use another database engine, like sqlite or something completly different to the one you are using in development, in my case I do that and I never commit the credentials to my real database, just the credentials to the test database, in this case with sqlite. You should check out the documentation for this kind of issues cause this is the right way to do that. – Dimitri Acosta Jul 29 '15 at 14:46
  • 2
    The problem with that is that I'm using SQL queries in my code (not using an ORM) so the queries could be subtly different on a different database engine. – Gnuffo1 Jul 31 '15 at 08:33
  • Maybe you've should start by mentioning that cause your questions is kind of ambiguous – Dimitri Acosta Jul 31 '15 at 14:32
  • 2
    .env.testing is an convention of Laravel? – przemo_li Jul 18 '19 at 13:42
  • 2
    Yes you could actually take a look at the documentation to learn more about the .env.testing file https://laravel.com/docs/5.8/testing – Dimitri Acosta Jul 25 '19 at 18:38
9

Create a local database on your dev machine, e.g. 'local_test_db'

Create a new .env.testing file.

DB_DATABASE=local_test_db
DB_USERNAME=root

Make sure your phpunit.xml file has at least this one env var:

<php>
    <env name="APP_ENV" value="testing"/>
</php>

Lastly your base testcase (TestCase.php) should run a migration to populate the db with tables:

public function createApplication()
{

    $app = require __DIR__.'/../bootstrap/app.php';

    $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();

    return $app;
}


public function setUp()
{
    parent::setUp();
    Artisan::call('migrate');
}

public function tearDown()
{
    Artisan::call('migrate:reset');
    parent::tearDown();
}
S..
  • 5,511
  • 2
  • 36
  • 43
  • "Make sure your phpunit.xml file has at least this one env var..." This is the one magical piece that Laravel documentation and other answers here don't make clear. Thanks very much! – Joel Mellon Oct 06 '21 at 15:02
  • @JoelMellon happy to help. – S.. Oct 07 '21 at 15:38
  • In my case, I use Pest testing. Applied .env.testing & updated phpunit.xml. Running `./vendor/bin/pest` works. Thanks for this. – josevoid Jun 29 '22 at 04:03
8

This is 2019.

I had this issues for so long until I was able to figure it out.

And here is my assumption:

If you are also finding it difficult to make your PHPUnit.xml talk with your .env.testing file, then you are likely using PHPStorm!

If this is true, continue reading.

If not, nope...this won't help.

Ok...

Here you go:

  1. Go to Settings of your PHPStorm or just simply press Ctrl + Alt + S.
  2. Go to Languages And Frameworks >> PHP >> Test Frameworks
  3. Under Test Runner tab, click Default configuration file and select (by clicking the folder icon) the path of your project's PHPUnit.xml file.

What this does is to make all your changes in the xml file take effect. So, go ahead, create the .env.testing file, create your preferred DB config variables for test...and try running your tests again!

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Wale
  • 1,321
  • 16
  • 11
5

From this link

Method 1

Step 1: Create New Test Database Connection on Database/Config.php as below:

return [
    ... 

    'default' => env('DB_CONNECTION', 'db'),    

    'connections' => [
        'sqlite_testing_db' => [
            'driver' => 'sqlite',
            'database' => storage_path().'/testing_database.sqlite',           
            'prefix' => '',
        ],

        /**************** OR ******************/

        'testing_db' => [
            'driver' => 'mysql',
            'host' => env('TEST_DB_HOST', 'localhost'),
            'database' => env('TEST_DB_DATABASE', 'forge'),
            'username' => env('TEST_DB_USERNAME', 'forge'),
            'password' => env('TEST_DB_PASSWORD', ''),
            'charset' => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix' => '',
            'strict' => false,
        ],

        /** Production or database DB **/
        'db' => [
            'driver' => 'mysql',
            'host' => env('TEST_DB_HOST', 'localhost'),
            'database' => env('TEST_DB_DATABASE', 'forge'),
            'username' => env('TEST_DB_USERNAME', 'forge'),
            'password' => env('TEST_DB_PASSWORD', ''),
            'charset' => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix' => '',
            'strict' => false,
        ],
    ],
];

Step 2: Specify the Database Credential on .env file

TEST_DB_HOST=localhost
TEST_DB_DATABASE=laravel
TEST_DB_USERNAME=root
TEST_DB_PASSWORD=rootwdp

Step 3: Specify test db conection to be used on phpunit.xml.

<env name="DB_CONNECTION" value="testing_db"/>
          OR Below If you prefer sqlite
<env name="DB_CONNECTION" value="sqlite_testing_db"/>                

Step 4: Migrate database to this new testing database - if you choose to use Database Transaction to Rollback insertion on the table.

php artisan migrate --database=testing_db

//If using sqlite
touch storage/testing_database.sqlite
php artisan migrate --database=sqlite_testing

Step 5: Now, the Unit test with Database Transaction looks like below:

<?php

use App\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class UserTest extends TestCase
{
    use DatabaseTransactions;

    /** @test */
    function it_test_user_can_be_saved()
    {
        factory(User::class, 2)->create();

        $users = User::all();

        $this->assertEquals(2, $users->count());
    }
}

//Run Php Unit
-> vendor/bin/phpunit --color tests/acceptance/model/UserTest.php

Note: If you prefer not to use Database Transaction, you can use setup and teardown method on TestCase.php class to migrate and rollback the database as below:

<?php

use Illuminate\Support\Facades\Artisan;

class TestCase extends Illuminate\Foundation\Testing\TestCase
{
    ...

    public function setUp()
    {
        parent::setUp();
        Artisan::call('migrate');
    }

    public function tearDown()
    {
        Artisan::call('migrate:reset');
        parent::tearDown();
    }
}
Sambhu Singh
  • 61
  • 1
  • 2
2

In your app.php change the Dotenv section

$envFile = 'testing' === env('APP_ENV') ? '.env.testing' : null;
try {
    (new Dotenv\Dotenv(__DIR__ . '/../', $envFile))->load();
} catch (Dotenv\Exception\InvalidPathException $e) {
    //
}

This will work hence PHPUnit changes the env before loading your app..so if running tests you will have the env already at testing

Julius Koronci
  • 408
  • 4
  • 10
  • Thanks, this answer worked for Lumen v5.6, the others not (maybe there are some differences while bootstrapping Lumen vs Laravel) – edrian Apr 22 '18 at 23:37
2

Been struggling with this for a few months now and just came across this Github issue today. From the solutions proposed there, here's what you should do in your CreatesApplication.php file (to delete the cached config in order to have Laravel load the test environment):

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

    $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();

    $this->clearCache(); // NEW LINE -- Testing doesn't work properly with cached stuff.

    return $app;
}

/**
 * Clears Laravel Cache.
 */
protected function clearCache()
{
    $commands = ['clear-compiled', 'cache:clear', 'view:clear', 'config:clear', 'route:clear'];
    foreach ($commands as $command) {
        \Illuminate\Support\Facades\Artisan::call($command);
    }
}

If you're still experiencing this issue after the above modification, you can go further by rebuilding the entire application as follows:

public function createApplication()
{
    $createApp = function() {
        $app = require __DIR__.'/../bootstrap/app.php';
        $app->make(Kernel::class)->bootstrap();
        return $app;
    };

    $app = $createApp();
    if ($app->environment() !== 'testing') {
        $this->clearCache();
        $app = $createApp();
    }

    return $app;
}

This is working just fine for me.

Leonel Elimpe
  • 194
  • 3
  • 22
2

Updated

For Laravel 5.8 users, you may create a .env.testing file in the root of your project.

Use a different db, like my_app_testing.

So, it will be, in .env

DB_DATABASE=clinical_managment

and in .env.testing

DB_DATABASE=clinical_managment_testing

Then, make config clear.

php artisan config:clear

Re-run the test. In my setup, it works.

Pyae Sone
  • 1,574
  • 2
  • 14
  • 18
1

I did all the steps in @Sambhu Singh answer as well as followed his link. But didn't work for me in L5.5

When migrating, adding/setting APP_ENV to 'testing' in front of the artisan command worked for me:

APP_ENV=testing php artisan migrate --database=sqlite_testing
Nisal Gunawardana
  • 1,345
  • 16
  • 20
0

I can't think of a way other than temporarily renaming .env.testing to .env before the unit tests start.

You could put some logic in bootstrap/autoload.php as this is what phpunit uses as it's bootstrap file before loading the application.

fire
  • 21,383
  • 17
  • 79
  • 114