Search code examples
phpphpunitphinx

How I can run specific phinx seeder and get the generated records in phpunit?


I made a phinx seeder:

use Phinx\Seed\AbstractSeed;

class ManagerRole extends AbstractSeed
{
    /**
     * Run Method.
     *
     * Write your database seeder using this method.
     *
     * More information on writing seeders is available here:
     * https://book.cakephp.org/phinx/0/en/seeding.html
     */
    public function run(): void
    {
        $prefix=microtime();
        $data=[
            [
                'email'=>$prefix."@example.com",
                'password'=>password_hash('1234',PASSWORD_DEFAULT),
                'active'=>true,
                'fullname'=> 'MANAGER Active'.$prefix,
                'role'=>'MANAGER'
            ],
            [
                'email'=>$prefix."[email protected]",
                'password'=>password_hash('1234',PASSWORD_DEFAULT),
                'active'=>false,
                'fullname'=> 'MANAGER Inactive'.$prefix,
                'role'=>'MANAGER',
                'activation_token'=>substr(base64_encode(random_bytes(12)),0,60);
            ]
        ];

        $this->insert('users', $data);
    }
}

And I made a basic Unit test class that runs phinx migrations:


namespace Tests;

use App\Application;
use Phinx\Config\Config;
use Phinx\Migration\Manager;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\NullOutput;

class DatabaseTestCase extends TestCase {

    private $pdo;

    private $migrationManager;
    
    public function setUp ()
    {
        // For rubistness we place the configuration here
        // We avoid using phinx.php    
        $migration_config = [
            'paths' => [
                'migrations' => __DIR__.'/../db/migrations',
                'seeds' => __DIR__.'/../db/seeds'
            ],
            'environments' => [
                'default_migration_table' => 'phinxlog',
                'default_environment' => 'testing',
                'testing' => [
                    'adapter' => 'mysql',
                    'host' =>  $_ENV['DB_HOST'],
                    'name' => $_ENV['DB_NAME'],
                    'user' => $_ENV['DB_USER'],
                    'pass' => $_ENV['DB_PASSWD'],
                    'port' => $_ENV['DB_PORT']??'3306',
                    'charset' => 'utf8',
                ],
            ],
            'version_order' => 'creation'
        ];

        // Configs are same
        $pdo = Application::createDB($migration_config['environments']['testing']);
        
        $config = new Config($migration_config);
        $manager = new Manager($config, new StringInput(' '), new NullOutput());
        $manager->migrate('testing');

        // You can change default fetch mode after the seeding
        $this->pdo = $pdo;
        $this->pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_OBJ);

        $this->migrationManager = $manager;
    }


    public function getMigrationManager()
    {
        return $this->migrationManager;
    }

    public function dbConnection()
    {
        return $this->pdo;
    }
}

That class I use it in order to make database tests:

class MyTest extends DatabaseTestCase
{
  public function test()
  {
    // Run seeder
    $manager = $this->getMigrationManager();

    $seededValues = // Get values from seeder
  }
}

Is there a way to do it?


Solution

  • I had a case as the one you mention, I needed to run the seeder upon a mysql db at my unit test, thus I did following steps:

    STEP1: Extending the Phinx\Db\Adapter\MysqlAdapter to accept an existing PDO connection*

    namespace Tests;
    
    use Phinx\Db\Adapter\MysqlAdapter;
    
    class TestAdapter extends MysqlAdapter
    {
        public function __construct(\PDO $connection)
        {
            $database = $connection->query('select database()')->fetchColumn();
            parent::__construct(['name'=>$database]);
            $this->setConnection($connection);
        }
    }
    

    Step 2 Run seeder Directly:

    // I assume that this test exists in `tests/MytestSuite`
    namespace Tests/MytestSuite;
    
    // Seeders are not autoloaded and autoloading may be unwanted
    // for example in production deployments
    include __DIR__."/../../db/seeds/ManagerRole.php";
    
    class MyTest extends DatabaseTestCase
    {
    
      private function makeSeeder():ManagerRole
      {
            $seeder = new ManagerRole();
            $connectionAdapter = new TestAdapter($this->dbConnection());
            $seeder->setAdapter($connectionAdapter);
    
            return $seeder;
      }
    
      public function test()
      {
        // Run seeder
        $manager = $this->getMigrationManager();
    
        // Do stuff here.
      }
    
    }
    
    

    With this you are able to run a phinx seeder upon phpunit tests.


    * I avoid using \Phinx\Db\Adapter\MysqlAdapter directly is because I get the following error:

    Warning: Undefined array key "name" in /var/www/html/vendor/robmorgan/phinx/src/Phinx/Db/Adapter/MysqlAdapter.php on line 240
    
    

    The reason why is despite setting the connection for example:

      $seeder = new CompletedForm();
    
      $connectionAdapter = new \Phinx\Db\Adapter\MysqlAdapter([]);
      $connectionAdapter->setConnection($this->dBConnection());
      $seeder->setAdapter($connectionAdapter);
    

    The MysqlAdapter still requires to know the database name. By extending it I query the database name from existing connection and set it in a reproducible manner.