Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

ritey's avatar
Level 2

Unit Testing using different sqlite file - connection override in model

I'm hoping someone can either explain what I'm doing wrong, or whether this is just the way it is. My setup is as follows:

  • Default connection string: default ( DB_CONNECTION env)
  • Alt connection string: alt
  • Testing connection string: testing

In a model class I use the protected $connection variable to set which connection to use when not using default so in some instances; $connection = 'alt';

With the unit testing, I have the phpunit.xml file with testing DB_CONNECTION override and I can see through debugging that it does switch to it as expected for testing, using the testing connection. However, because of the set connection in the model, this seems to be taking precedent. In the unit test I've even tried using $model->setConnection('testing')->all(); for example and it still uses the alt connection string even if I debug $model->setConnection('testing'); which says in the debug that the connection is "testing".

So for example in the alt connection on a model I have say 23 records, in testing I have 5 seeded, the unit test always returns the 23 count not 5.

Is this usual behaviour, is there something I'm missing?

Thanks

0 likes
12 replies
ritey's avatar
Level 2

Thanks SaeedPrez, it's not the method call that's at issue, it's using totally the wrong db because of the specified connection in the model class and not the testing connection (all tables in one db) that I want it to use for unit tests.

Does that make sense?

Thanks

SaeedPrez's avatar

I understand, ->all() seems to be ignoring the ->setConnection()...

Anyways, I wouldn't use ->setConnection() for this, instead I would create a trait and use it in all models where I need the alt connection. With some simple logic you could check if it's testing, if so protected $connection = 'testing' else protected $connection = 'alt'.

This way you don't have to repeat your code and you have all your connection logic all in one place.

3 likes
ritey's avatar
Level 2

Perhaps some code will help me explain it a little better.

PHPUNIT XML

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

The class model

class Applicant extends Model
{
    /**
    * The table associated with the model.
    *
    * @var  string
    */
    protected $table = 'applicants';
    
    /**
    * The attributes that are mass assignable.
    *
    * @var  array
    */
    protected $fillable = [
        'phone',
        'passcode'
    ];
}

The test:


    $applicant = [
            'phone' => '447777777777',
        ];

        Applicant::create($applicant);

        $applicants = Applicant::all(); // uses default testing connection 
    as no specific connection is mentioned on the model

        $this->assertCount(6,$applicants); // seeded with 5 originally 
    so 1 addition is 6 and passes

All works correctly because no connection was specified in the model, so the default DB_CONNECTION in the testing environment was used as expected. The actual environment uses multiple connections though, so given that:

The class model

class Applicant extends Model
{

    /**
    * The database connection used with the model.
    *
    * @var  string
    */
    protected $connection = 'alt';
    /**
    * The table associated with the model.
    *
    * @var  string
    */
    protected $table = 'applicants';
    
    /**
    * The attributes that are mass assignable.
    *
    * @var  array
    */
    protected $fillable = [
        'phone',
        'passcode'
    ];
}

The test:


    $applicant = [
            'phone' => '447777777777',
        ];

        Applicant::create($applicant);

        $applicants = Applicant::all(); //uses the alt connection and 
    not the testing connection despite the PHPUNIT XML 
    vars, the model overrides the connection type

        $this->assertCount(6,$applicants); // seeded with 5 originally 
    should be 6 but could be something like 233 because it's 
    not using the known world database (seeded test db)

To get around this for now, I've done:

    if (env('APP_ENV') != 'testing') {
        /**
        * The database connection used with the model.
        *
        * @var  string
        */
        protected $connection = 'alt';
    }

This to me seems wrong but it gets around using multiple connections on the real environment and passing unit tests on 1 single testing connection.

SaeedPrez's avatar

@ritey

That looks good actually,..

  1. how many models use the alt connection?
  2. where do you have the alt connection code, in a trait or did you put it in every model?
ritey's avatar
Level 2

It's in a trait for now but I just thought there is probably a more "correct" way of doing it. Currently default has about 20 models, and two other db's have around 8 each. The trait removes a level of redundancy but I'm surprised that the testing connection doesn't override those set on the model, do we think it should or shouldn't? Or perhaps prefixing the connections and having the same multiple db's in testing is the correct way to go?

SaeedPrez's avatar

@ritey I'm not sure if you read what I wrote about the bug (or maybe it's designed this way).

Anyways, when I tried this, I could use setConnection('testing')->get() instead of setConnection('testing')->all() and it worked in Laravel 5.2.31.

Why aren't you sure if using a trait is the "correct" way? Is it because it's bad practice to have the connection logic all in one place? Or maybe it's bad practice to be able to easily switch connection on all the affected models? I'm curious why you're not sure..

ritey's avatar
Level 2

@SaeedPrez Thanks, yes I saw the bug but it's not exactly what my problem is as I used setConnection in the test just to see if it did override but it didn't, it's not that I wanted to use it. I expected the testing environment to use the testing connection for all connections despite having a connection set on a particular model.

To me using the trait on the models to change the connection based on environment doesn't feel the natural way of doing it, even if it does work. I would of thought it should be linked to the testing setup whether that be in the phpunit.xml or within a testcase bootstrap method or something like that, not on a model class.

1 like
SaeedPrez's avatar

@ritey the model $connection property seems to have priority over the env connection settings, which is a good thing. But in your case I can see how it causes unwanted results.

Using traits to get around this is not a bad practice as far as I'm concerned. And if you find a better way, it wouldn't be hard or take long to replace it, so I wouldn't worry about it.

coxy121's avatar

@ritey @SaeedPrez I have a similar issue myself and your solution of using a trait looks like it should work well.

However I have a problem when using DatabaseMigrations,

In my migration files i specify the connection as I use multiple database within the same app, so have migrations for tables in the various database. So my users table migration would look like:

Schema::connection('database_a')->create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });

When running phpunit with DatabaseMigrations the connection is not being overridden with the testing connection and I get the error

Table 'users' already exists

Did you experience this issues, and/or do you have any advice suggestions on how to resolve it?

kitloong's avatar

@COXY121 - Sorry for reply an old thread, just to add some remarks in case others have same question

You could use php artisan migrate --database="mysql.db2"

root@52a61da0c5dc:/var/www/code# php artisan migrate -h
Description:
  Run the database migrations

Usage:
  migrate [options]

Options:
      --database[=DATABASE]  The database connection to use
      --force                Force the operation to run when in production
muhammad_zeshan's avatar

@SAEEDPREZ - @saeedprez Your approach looks exciting to me.

However I'm wondering how to set model's connection in a trait's function?

There probably be an easy way to achieve this but looks like I'm lost here.

Thanks for help in advance!

Please or to participate in this conversation.