dpeqsfx5186 2019-02-05 13:33
浏览 53

Symfony 4:在test + controller + twig扩展中保持SQLite PDO连接

My situation

I have a Symfony 4.2 project with the following structure:

  • src
    • Controller
    • Service
    • Twig Extensions
  • Templates
  • Tests

I use a database class, which internally creates a PDO connection. If i run my tests with PHPUnit, my database class has to switch from mysql to sqlite. Everything works fine here.

My problem

I can not keep the once created Database instance, when running just one test. Symfony seems to recreate it during the test: when inside a Twig template which uses a Twig extension. Because the database class uses

new \PDO('sqlite::memory:');

it loses created tables and therefore the test fails. I am aware of, that the Database instance (with PDO reference) gets reseted after each test, but in my situation i only have one test. How can i ensure, that it re-uses the Database instance?

Here the related code

InstanceExtension class provides the function title, which is used in a Twig template and has to access the database.

<?php

namespace App\TwigExtension;

use App\Service\Database;
use Twig\TwigFunction;
use Twig\Extension\AbstractExtension;

class InstanceExtension extends AbstractExtension
{
    protected $db;

    /**
     * @param Database $db
     */
    public function __construct(Database $db)
    {
        $this->db = $db;
    }

    /**
     * Tries to return the title/name for a given ID.
     *
     * @param string $subject
     * @param string|array $tables
     * @param string $lang         Default is 'de'
     *
     * @return string label or id, if no title/name was found
     */
    public function title(string $subject, $tables, string $lang = 'de'): string
    {    
        return $this->db->get_instance_title($subject, $tables, $lang);
    }
}

In my services.yaml the Database class is set to public (which should enable reusing it, doesn't it?):

App\Service\Database:
    public: true

Database class

Here is the part of the Database class which initializes the PDO connection. Production code, which uses MySQL instead, removed:

<?php

declare(strict_types=1);

namespace App\Service;

class Database
{
    /**
     * @param Config $app_config
     *
     * @throws \Exception
     */
    public function __construct(Config $app_config)
    {
        global $sqlite_pdo;

        try {
            // non test
            // ..

            // test environment
            } else {
                $this->db_engine = 'sqlite';

                // workaround to ensure we have the same PDO instance everytime we use the Database instance
                // if we dont use it, in Twig extensions a new Database instance is created with a new SQLite
                // database, which is empty.
                if (null == $sqlite_pdo) {
                    $pdo = new \PDO('sqlite::memory:');
                    $sqlite_pdo = $pdo;
                } else {
                    $pdo = $sqlite_pdo;
                }
            }

        } catch (\PDOException $e) {
            if (\strpos((string) $e->getMessage(), 'could not find driver') !== false) {
                throw new \Exception(
                    'Could not create a PDO connection. Is the driver installed/enabled?'
                );
            }

            if (\strpos((string) $e->getMessage(), 'unknown database') !== false) {
                throw new \Exception(
                    'Could not create a PDO connection. Check that your database exists.'
                );
            }

            // Don't leak credentials directly if we can.
            throw new \Exception(
                'Could not create a PDO connection. Please check your username and password.'
            );
        }

        if ('mysql' == $this->db_engine) {
            // ...
        }

        // default fetch mode is associative
        $pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);

        // everything odd will be handled as exception
        $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);

        $this->db = $pdo;
    }

    // ...
}

One test looks kinda like:

<?php 

class SalesChannelControllerTest extends TestCase
{
    public function setUp()
    {
        parent::setUp();

        // init Database class, using SQLite
        $this->init_db();

        // further setup function calls

        // SalesChannelController is a child of
        // Symfony\Bundle\FrameworkBundle\Controller\AbstractController
        $this->fixture = new SalesChannelController(
            $this->db
            // ...
        );
    }

    /**
     * Returns a ready-to-use instance of the database. Default adapter is SQLite.
     *
     * @return Database
     */
    protected function init_db(): Database
    {
        // config parameter just contains DB credentials
        $this->db = new Database($this->config);
    }

    public function test_introduction_action()
    {
         // preparations

         /*
          * run action
          *
          * which renders a Twig template, creates a Response and returns it.
          */
         $response = $this->fixture->introduction_action($this->request, $this->session, 'sales_channel1');

         /*
          * check response
          */
         $this->assertEquals(200, $response->getStatusCode());
    } 
}

My current workaround is to store the PDO instance in a global variable and re-use it, if required.

<?php 

global $sqlite_pdo;

// ...

// inside Database class, when initializing the PDO connection
if (null == $sqlite_pdo) {
    $pdo = new \PDO('sqlite::memory:');
    $sqlite_pdo = $pdo;
} else {
    $pdo = $sqlite_pdo;
}

If you need more info, please tell me. Thanks for your time and help in advance!

  • 写回答

0条回答 默认 最新

    报告相同问题?

    悬赏问题

    • ¥15 乘性高斯噪声在深度学习网络中的应用
    • ¥15 运筹学排序问题中的在线排序
    • ¥15 关于docker部署flink集成hadoop的yarn,请教个问题 flink启动yarn-session.sh连不上hadoop,这个整了好几天一直不行,求帮忙看一下怎么解决
    • ¥30 求一段fortran代码用IVF编译运行的结果
    • ¥15 深度学习根据CNN网络模型,搭建BP模型并训练MNIST数据集
    • ¥15 C++ 头文件/宏冲突问题解决
    • ¥15 用comsol模拟大气湍流通过底部加热(温度不同)的腔体
    • ¥50 安卓adb backup备份子用户应用数据失败
    • ¥20 有人能用聚类分析帮我分析一下文本内容嘛
    • ¥30 python代码,帮调试,帮帮忙吧