PHPUnit - утверждение, что запрос возвращает хорошие данные из базы данных - PullRequest
2 голосов
/ 23 марта 2011

Я тестирую Фабрику, которая просто извлекает все «сообщения» из новостной системы.Я приведу пример к чему-то настолько простому, насколько это возможно:

$newsFactory->getAllNews();

Таблица выглядит следующим образом:

+---------+---------------------+-------------+
| news_id | news_publishedDate  | news_active |
+---------+---------------------+-------------+
|       1 | 2010-03-22 13:20:22 |           1 |
|       2 | 2010-03-23 13:20:22 |           1 |
|      14 | 2010-03-23 13:20:22 |           0 |
|      15 | 2010-03-23 13:20:22 |           1 |
+---------+---------------------+-------------+

Я хочу проверить это поведение;сейчас мы сосредоточимся только на первом:

  • Убедитесь, что запрос будет возвращать только news_active = 1
  • Убедитесь, что запрос вернет элемент, упорядоченный по news_publishedDate,от самого нового к старшему.

Итак, я сделал dbData.xml набор данных, который я считаю хорошими данными тестирования:

<?xml version="1.0" encoding="UTF-8" ?>
<dataset>
  <table name="news">
    <column>news_id</column>
    <column>news_publishedDate</column>
    <column>news_active</column>
    <row>
        <value>1</value>
        <value>2010-03-20 08:55:05</value>
        <value>1</value>
    </row>
    <row>
        <value>2</value>
        <value>2010-03-20 08:55:05</value>
        <value>0</value>
    </row>
    <row>
        <value>3</value>
        <value>2011-03-20 08:55:05</value>
        <value>1</value>
    </row>
  </table>
</dataset>

Хорошо, так что давайте просто проверим первыйtest (не возвращая news_id # 2 из набора данных XML)

Я должен расширить класс PHPUnit_Extensions_Database_TestCase, чтобы сделать мой класс NewsFactoryTest:

<?php
require_once 'PHPUnit/Extensions/Database/TestCase.php';

class NewsFactoryTest extends PHPUnit_Extensions_Database_TestCase
{
    protected $db;


    protected function getConnection()
    {
        $this->db = new PDO('mysql:host=localhost;dbname=testdb', 'root', '');
        return $this->createDefaultDBConnection($this->db, 'testdb');
    }

    protected function getDataSet()
    {
        return $this->createXMLDataSet(dir(__FILE__) . DIRECTORY_SEPARATOR . 'dbData.xml');
    }

    public function testGetNewsById()
    {
        $newsFactory = new NewsFactory($this->db);
        $news = $newsFactory->getNewsById();
        // ???
        $this->assertEquals(2, count($news), "Should return only 2 results");
    }
}

Мой главный вопрос будет как мне настроить этот тест?

В деталях я пытаюсь понять:

  • Должен ли я создать базу данных testdb или это все эмулируется / виртуально?
    • Я видел много примеров использования sqlite::memory:, это хорошая идея для тестирования MySQL-запросов с помощью sqlite?Могу ли я использовать mysql::memory: вместо этого?
    • Если это реальная БД, как мне восстановить все данные из dbData.xml в БД перед каждым запуском теста?
  • Куда мне звонить getConnection() и getDataSet()?

Спасибо за чтение и обмен знаниями!

Ответы [ 3 ]

3 голосов
/ 01 апреля 2011

Я настроил тестирование базы данных в нашем проекте, и вот некоторые ответы и уроки, которые сработали для нас:

Должен ли я создать базу данных testdb или это все эмулируется / виртуально?

Я задавал один и тот же вопрос в начале, и мы оба узнали, что это действительно настоящая база данных.

Я видел многопримеры, использующие sqlite :: memory:, это хорошая идея для тестирования MySQL-запросов с sqlite?Могу ли я использовать mysql :: memory: вместо этого?

Я попытался использовать sqlite для повышения производительности, но обнаружил, что SQL будет достаточно разным, чтобы его нельзя было везде использовать с нашим существующим кодом.Мне удалось использовать механизм MySQL MEMORY для большинства представленных таблиц (это невозможно для некоторых таблиц, таких как столбцы BLOB).

Если это настоящая БД, как мне восстановить все данные?из dbData.xml в БД перед каждым запуском теста?

Я написал скрипт для вызова mysqldump схем и всех их таблиц с нашего удаленного тестового сервера, вставил их на локальный сервери преобразовать все возможные движки таблиц в ПАМЯТЬ.Это требует времени, но поскольку схемы не меняются между тестами, они запускаются только один раз в самом верху TestSuite или по отдельности, как того требует разработчик в своей локальной системе.

Наборы данных загружаются в началекаждого теста и поскольку таблица уже существует и находится в памяти, вставка и усечение между тестами происходит быстро.

Где я должен вызывать getConnection () и getDataSet ()?

У нас уже был вспомогательный класс, расширяющий TestCase, поэтому я не мог использовать PHPUnit_Extensions_Database_TestCase.Я добавил функции настройки в этот вспомогательный класс и никогда не вызывал и не должен был реализовывать getDataSet ().Я использовал getConnection () для создания наборов данных из измененных данных в функции assert.

/**
 * @param PHPUnit_Extensions_Database_DataSet_IDataSet $expected_data_fixture
 * @param string|array $tables
 */
protected function assertDataFixturesEqual($expected_data_fixture, $tables){
    if(!is_array($tables)){
        $tables = array($tables);
    }
    PHPUnit_Extensions_Database_TestCase::assertDataSetsEqual($expected_data_fixture, $this->DbTester->getConnection()->createDataSet($tables));
}

РЕДАКТИРОВАТЬ: я обнаружил, что некоторые закладки ресурсов, которые я использовал в документации PHPUnit, немного не хватает:

http://www.ds -o.com / archives / 63-PHPUnit-Database-Extension-DBUnit-Port.html

http://www.ds -o.com / archives / 64-Добавление-Database-тесты-к-Existing-PHPUnit-Test-Cases.html

0 голосов
/ 24 марта 2011

До сих пор я понял, что: Должен ли я создать базу данных testdb или это все эмулируется / виртуально?

Это создает реальную базу данных и, используя метод getSetUpOperation, этоочень медленный, поскольку таблицы усекаются и повторно импортируются для каждого теста, и это требует большого количества жесткого диска даже для небольшого объема данных.(~ 1 сек / тест)

Я видел много примеров использования sqlite :: memory:, это хорошая идея для тестирования запросов на основе MySQL с sqlite?Могу ли я использовать mysql :: memory: вместо этого?

Я до сих пор не знаю.Я думаю, что теперь это действительно возможно с MySQL.

Если это настоящая БД, как мне восстановить все данные из dbData.xml в БД перед каждым запуском теста?

Есть getSetUpOperation и getTearDownOperation, которые действуют как метод setup и tearDown.Добавление этого приведет к обрезанию таблицы, упомянутой в dataSet, и повторной вставке всех данных этого XML-файла:

/**
 * Executed before each
 *
 * @return PHPUnit_Extensions_Database_Operation_DatabaseOperation
 */
protected function getSetUpOperation()
{
    return PHPUnit_Extensions_Database_Operation_Factory::CLEAN_INSERT();
}

Где я должен вызывать getConnection () и getDataSet ()?

Нигде.Тезисы - магический метод, который вызывается автоматически.getConnection вызывается перед тестами (немного похоже на __construct, но я не уверен насчет порядка), а getDataSet будет вызываться, когда нужен dataSet.Я думаю , что в моем случае только getSetUpOperation имеет зависимость для dataSet ... поэтому в фоновом режиме он вызывает метод getDataSet перед каждым тестом для выполнения операции CLEAN_INSERT.

Кроме того,Я обнаружил, что нам нужно создать структуру таблицы (набор данных не обрабатывает это), поэтому мой полный --slow- рабочий код:

<?php
require_once 'PHPUnit/Extensions/Database/TestCase.php';

class NewsFactoryTest extends PHPUnit_Extensions_Database_TestCase
{
/**
 * Custom PDO instance required by the SUT.
 *
 * @var Core_Db_Driver_iConnector
 */
protected $db;

/**
 * Create a connexion.
 * Note: les constantes de connexion sont définit dans bootstrap.php.
 * 
 * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection
 */
protected function getConnection()
{

    //Instanciate the connexion required by the system under test.
    $this->db = new Core_Db_Driver_PDO('mysql:host=' . TEST_DB_HOST . ';dbname=' . TEST_DB_BASE, TEST_DB_USER, TEST_DB_PASS, array());

    //Create a genuine PDO connexion, required for PHPUnit_Extensions_Database_TestCase.
    $db = new PDO('mysql:host=' . TEST_DB_HOST . ';dbname=' . TEST_DB_BASE, TEST_DB_USER, TEST_DB_PASS);
    $this->createTableSchema($db);
    return $this->createDefaultDBConnection($db, TEST_DB_BASE);
}

/**
 * Load the required table schemes.
 *
 * @param PDO $db
 * @return void
 */
protected function createTableSchema(PDO $db)
{
    $schemaPath = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'sql_schema' . DIRECTORY_SEPARATOR;
    $query = file_get_contents($schemaPath . 'news.sql');

    $db->exec($query);

    $query = file_get_contents($schemaPath . 'news_locale.sql');
    $db->exec($query);
}

/**
 * Load the dataSet in memory.
 *
 * @return PHPUnit_Extensions_Database_DataSet_IDataSet
 */
protected function getDataSet()
{
    return $this->createXMLDataSet(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'newsFactory_dataSet.xml');
}

/**
 * Method executed before each test
 *
 * @return PHPUnit_Extensions_Database_Operation_DatabaseOperation
 */
protected function getSetUpOperation()
{
    //TRUNCATE the table mentionned in the dataSet, then re-insert the content of the dataset.
    return PHPUnit_Extensions_Database_Operation_Factory::CLEAN_INSERT();
}

/**
 * Method executed after each test
 *
 * @return PHPUnit_Extensions_Database_Operation_DatabaseOperation
 */
protected function getTearDownOperation()
{
    //Do nothing ( yup, their's a code for that )
    return PHPUnit_Extensions_Database_Operation_Factory::NONE();
}


/**
 * @covers NewsFactory::getNewsById
 */
public function testGetNewsById()
{
    $newsFactory = new NewsFactory($this->db);
    $news = $newsFactory->getNewsById(999);
    $this->assertFalse($news);
}

}

Надеюсь, что это поможет другим людям, которым нужны некоторыедополнительные объяснения.Если у вас есть какие-либо комментарии, предложения или идеи, ваш вклад приветствуется, так как я не считаю это решение полностью эффективным.(Медленно и долго настраивается и требует двойного соединения.)

0 голосов
/ 24 марта 2011

Я не использовал контрольный пример базы данных PHPUnit, поэтому я должен ограничить свой ответ утверждением.Вы можете либо утверждать, что ID 2 отсутствует в $news, либо вы можете утверждать, что каждый объект в $news неактивен.Последнее является более гибким, поскольку вам не нужно менять тест при добавлении данных в набор тестовых данных.

$news = $newsFactory->getNewsById();
foreach ($news as $item) {
    self::assertTrue($news->isActive());
}

Кстати, все опубликованные даты в вашем наборе данных идентичны.Это сделает тестирование заказа невозможным.;)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...