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


PHPUnit database test: integer data type is returned as string

这是我的测试,不像预期的那样工作。我的测试类扩展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();
        }
    }
}

My Number Domain Model是这样的:

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;
    }
}

我找到了解决问题的方法,尽管如此,我仍然不知道为什么我必须这样做。我注意到我的小脚本和测试代码确实存在差异。在我的脚本中,我有一些PDO属性设置,我在测试中也没有设置。PDO:: attr_emulate_preparades 属性是重要的一个。一旦我在测试中将其设置为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;
}