4
\$\begingroup\$

I am making a base test class that bootstraps database Test cases

namespace Tests

use App\DB\DBFactory;
use Phinx\Config\Config;
use Phinx\Migration\Manager;
use PHPUnit\Framework\TestCase;

class DatabaseTestCase extends TestCase
{

    private PDO $pdo;

    private $migrationManager;

    public function setUp(): void
    {
        parent::setUp();

        error_reporting(E_ALL);

        /**
         * For robustness we place the configuration here
         * We avoid using phinx.php cause I want to have fixed settings for db
         */
        $migration_config = [
            'paths' => [
                'migrations' => __DIR__.'/../db/migrations',
                'seeds' => __DIR__.'/../db/seeds'
            ],
            'environments' => [
                'default_migration_table' => 'phinxlog',
                'default_environment' => 'testing',
                'testing' => [
                    'adapter' => 'mysql',
                    'host' => $_ENV['MYSQL_HOST'],
                    'name' => $_ENV['MYSQL_DATABASE'],
                    'user' => $_ENV['MYSQL_USER'],
                    'pass' => $_ENV['MYSQL_PASSWORD'],
                    'port' => $_ENV['DB_PORT']??'3306',
                    'charset' => 'utf8',
                ],
            ],
            'version_order' => 'creation'
        ];

        // Configs need to be the same for both application and tests.
        $pdo = self::createTestDb($migration_config,$new_config);
        
        $config = new Config($new_config);
        $manager = new Manager($config, new StringInput(' '), new NullOutput());
        $this->migrationManager = $manager;

        $this->migrationManager->migrate('testing');

        // You can change default fetch mode after the seeding
        $this->pdo = $pdo;
    }

    public function tearDown():void
    {
        try{
            $testDbName = $this->pdo->query('select database()')->fetchColumn();
            $this->pdo->query("DROP DATABASE `" . str_replace('`', '``', $testDbName) . "`");
        }catch (\Exception $e){
        }

        parent::tearDown();
    }
}

I create the PDO connection as:

namespace App\DB;

class DBFactory
{
    /**
     * Setups a New Db service from config
     * @return \PDO
     */
    public static function createDbConnectionInstance(array $config):\PDO
    {
        $connectionString = 'mysql:host=%s;port=%s;dbname=%s';
        $dbPort = !empty($config['port'])?$config['port']:3306;
        $dsn = sprintf($connectionString,$config['host'],$dbPort,$config['name']);

        $pdo = new \PDO($dsn,$config['user'],$config['pass']);
        $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
        return $pdo;
    }
}

The idea to use it is just extend the DatabaseTestCase In case that some of my tests require a DB Connection and a Database:

namespace Tests;

use Tests\DatabaseTestCase;

class DummyTest extends DatabaseTestCase
{
    public function testDump()
    {
        // 
        $this->assertTrue(true);
    }
}

And upon each test a new Db is created. The reason why I implemented this is because I need to parallelize my tests and some of them require Db connection (for example to ensure that records are properly written or modified upon DB).

But what the downsides in this approach? Is this a good approach?

\$\endgroup\$
2
  • 2
    \$\begingroup\$ Please do not edit the question, especially the code, after an answer has been posted. Changing the question may cause answer invalidation. Everyone needs to be able to see what the reviewer was referring to. What to do after the question has been answered. \$\endgroup\$
    – pacmaninbw
    Commented Oct 8 at 12:51
  • \$\begingroup\$ Upon createTestDb I give the existing config and I update the config with the new databaseName. The $new_config is the new configuration. Ommited for simplicity. \$\endgroup\$ Commented Oct 9 at 12:59

1 Answer 1

2
\$\begingroup\$

The OP + Review Context don't explain where the input parameter $new_config came from. But that's fine, I'm sure it appears elsewhere in your codebase.

swallowed exception

This seems bad.

            $this->pdo->query("DROP DATABASE `" . str_replace('`', '``', $testDbName) . "`");
        } catch (\Exception $e) {
        }

It would be much better to not catch at all, and simply let any exceptions bubble up the call stack. Silently discarding is usually appropriate just for "expected exception" statements. If an "always succeeds" statement like that DROP turns out to fail, I will want to know about so I can diagnose whatever changed.

string interpolation

Little Bobby Tables advises us that being in the habit of routinely adjusting quoted commands and then executing them is bound to lead to sorrow. Prefer to use bind parameters.

\$\endgroup\$
5
  • \$\begingroup\$ The reason why is because using prepared statements does quote the database name and fails to be executed. I mean I tried but the prepared statemens seem to work for Select , Delete, Update, Insert statements. \$\endgroup\$ Commented Oct 8 at 7:18
  • \$\begingroup\$ Also If I fal tio drop the db I create a new one upon setup therefore I want to ignore in order to terminate the test. At tearDown test is executed nevertheless and this is the supportive code tbw. \$\endgroup\$ Commented Oct 8 at 7:20
  • 1
    \$\begingroup\$ Also regarding prepared statements I approached like this because I faced this issue: stackoverflow.com/q/79064837/4706711 \$\endgroup\$ Commented Oct 8 at 7:44
  • \$\begingroup\$ I updated the code so I can explain why I have $newconfig. \$\endgroup\$ Commented Oct 8 at 7:46
  • \$\begingroup\$ the Bobby tables reference seems off the track here, made the OP to waste quite a time \$\endgroup\$ Commented Oct 8 at 15:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.