PHPUnit数据库测试:整数数据类型以字符串形式返回

时间:2014-07-16 17:23:22

标签: php database phpunit

这是我的测试没有按预期工作。我的测试类扩展了 PHPUnit_Extensions_Database_TestCase ,在每次测试运行之前,所有表都被截断并填充下面显示的数据集。

class NumberMapperTest extends PHPUnit_Extensions_Database_TestCase {

    private $dsn = 'mysql:host=...;dbname=test;port=3306;charset=utf8';
    private $conn = null;

    private $numberMapper;

    protected function getConnection() {
        if ($this->conn === null) {
            $pdo = new PDO($this->dsn, '...', '...');
            $this->conn = $this->createDefaultDBConnection($pdo, 'test');
        }

        return $this->conn;
    }

    public function getDataSet() {
        $compositeDs = new PHPUnit_Extensions_Database_DataSet_CompositeDataSet(array());
        $ds = $this->createMySQLXMLDataSet('fixtures/number.xml');
        $compositeDs->addDataSet($ds);
        return $compositeDs;
    }

    public function setUp() {
        $conn = $this->getConnection();
        $pdo = $conn->getConnection();

        $pdo->exec('SET foreign_key_checks = 0');
        parent::setUp();
        $pdo->exec('SET foreign_key_checks = 1');

        $databaseAdapter = new DatabaseAdapter($pdo);
        $this->numberMapper = new NumberMapper($databaseAdapter);
    }

    public function tearDown() {
        $conn = $this->getConnection();
        $pdo = $conn->getConnection();

        $pdo->exec('SET foreign_key_checks = 0');
        parent::tearDown();
        $pdo->exec('SET foreign_key_checks = 1');
    }

    protected function getTearDownOperation() {
        return PHPUnit_Extensions_Database_Operation_Factory::TRUNCATE();
    }

    /**
     * @test
     * @covers Classes\Mapper\NumberMapper::findByUid
     */
    public function findByUidReturnsExpectedNumber() {
        $expectedNumber = new Number(1, 'AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA');
        $this->assertEquals($expectedNumber, $this->numberMapper->findByUid('AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA'));
    }
}

测试引发以下异常:

InvalidArgumentException: The number ['1'] is invalid.

正如你在''(引号)上看到的那样,数字是以字符串而不是数字的形式返回的,我不知道为什么。因为它存储为测试数据库中的数字。我可以确认,在测试运行后没有截断表,并使用一个小脚本连接到我的测试数据库并运行它。一切都按预期工作 - 创建一个Number对象。

<?php
...
    $adapter = new DatabaseAdapter($pdo);
    $numberMapper = new NumberMapper($adapter);
    var_dump($numberMapper->findByUid('AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA'));

所以我的代码似乎工作正常,但我的测试失败(抛出异常)。我发现无法为我的测试数据集中的列指定数据类型。也许存在问题,因为看起来所有值都被视为字符串。但正如所说的那样,数字作为数字正确存储到数据库中。有人可以解释这种行为的原因是什么,因为我很无能。

我的测试数据库有一个带有以下模式的数字表:

CREATE TABLE `number` (
    `uid` CHAR(36) NOT NULL DEFAULT '',
    `number` INT(10) UNSIGNED NOT NULL,
    PRIMARY KEY (`uid`),
    UNIQUE INDEX `number` (`number`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;

要使用PHPUnit测试我的NumberMapper,我使用了以下数据集[fixtures / number.xml]:

<?xml version="1.0"?>
<mysqldump xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<database name="test">
    <table_data name="number">
        <row>
            <field name="uid">AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA</field>
            <field name="number">1</field>
        </row>
    </table_data>
</database>
</mysqldump>

我要测试的NumberMapper:

class NumberMapper {

    private $adapter;

    public function __construct($adapter) {
        $this->adapter = $adapter;
    }

    public function findByUid($uid) {
        $this->adapter
             ->select(array('uid', 'number'))
             ->from('number')
             ->where('uid = :uid')
             ->bindParameters(array(':uid' => $uid));
        return $this->createEntity($this->adapter->fetch());
    }

    public function createEntity(array $data) {
        if (isset($data['number'])) {
            $uid = isset($data['uid']) ? $data['uid'] : $this->generateUid();
            return new Number($data['number'], $uid);
        } else {
            return new NullNumber();
        }
    }
}

我的号码域模型如下所示:

class Number {

    /**
     * @var string
     */
    private $uid;

    /**
     * @var int
     */
    private $number;

    public function __construct($number, $uid) {
        $this->setUid($uid);
        $this->setNumber($number);
    }

    public function setUid($uid) {
        if (!is_string($uid) || strlen($uid) !== 36) {
            throw new InvalidArgumentException('The uid [' . var_export($uid, TRUE) . '] is invalid.');
        }
        $this->uid = $uid;
    }

    public function setNumber($number) {
        if (is_numeric($number) && !is_string($number) && 0 <= $number && $number <= 4294967295 === FALSE) {
            throw new InvalidArgumentException('The number [' . var_export($number, TRUE) . '] is invalid.');
        }
        $this->number = $number;
    }
}

1 个答案:

答案 0 :(得分:0)

我找到了解决问题的方法,所以我仍然不知道为什么我必须这样做。我注意到我的小脚本和测试代码确实存在差异。在我的脚本中,我有一些PDO属性集,我在测试中也设置了这些属性集。 PDO :: ATTR_EMULATE_PREPARES 属性是重要的属性。一旦我在测试中将其设置为false,一切都按预期工作。我现在也在我的测试中设置 ATTR_ERRMODE ,以便我的getConnection方法现在看起来像这样:

protected function getConnection() {
    if ($this->conn === null) {
        $pdo = new PDO($this->dsn, '...', '...');
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
        $this->conn = $this->createDefaultDBConnection($pdo, 'test');
    }

    return $this->conn;
}