Как вы делаете модульное тестирование с приложением, которое использует ORM? - PullRequest
2 голосов
/ 23 марта 2009

Я просмотрел различные вопросы по модульному тестированию, но не могу найти тот, который конкретно отвечает на этот вопрос.

У меня есть несколько классов PHP, которые содержат функции, которые выглядят так:

    static function _setSuspended($Suspended, $UserID)
    {
        try {
            $con = Propel::getConnection();

            $c1 = new Criteria();
            $c1->add(DomainsPeer::USERID,$UserID);

            $update = new Criteria();
            $update->add(DomainsPeer::SUSPENDED,$Suspended);

            BasePeer::doUpdate($c1, $update, $con);

            return true;
        } catch(PropelException $e) {
            return $e->getMessage();
        }
    }

Я использую Propel в качестве своего ORM. Я прочитал различные темы модульного тестирования, в которых говорится о создании «Mocks» и «Stubs», а что нет, но я не смог найти ничего, что конкретно скажет вам, как протестировать функцию, как описано выше.

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

Я просто не вижу способа сделать это. Что мне здесь не хватает?

Ответы [ 5 ]

2 голосов
/ 23 марта 2009

Это общий ответ в том смысле, что я совсем не знаком с Propel и лишь немного более знаком с PHP. Основной ответ заключается в том, что вы используете внедрение зависимостей. Вместо того, чтобы обращаться непосредственно к вашему ORM, вы создаете обертку вокруг него, а затем внедряете обертку в ваш класс / функцию для фактического использования. Чтобы выполнить модульное тестирование, вы создаете фиктивную или поддельную версию оболочки, которая не взаимодействует с ORM, но вместо этого позволяет настраивать ответы от оболочки на вызовы ваших методов. Это позволяет вам выделить ORM при модульном тестировании ваших функций.

1 голос
/ 13 декабря 2011

Я пытался решить ту же проблему при создании плагина PHPUnit для Symfony . Я подошел к нему так же, как к тестовой среде Django - использую отдельную базу данных / соединение, и уничтожаю и перестраиваю ее перед каждым тестом.

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

1 голос
/ 23 марта 2009

Я обнаружил, что насмешка над ORM не дает мне никакой уверенности, потому что конфигурация ORM никогда не тестируется. ORM также имеют много действий на расстоянии, которые могут дать ложную уверенность при юнит-тестах. Насмешка над драйвером базы данных или предоставление альтернативной базы данных в памяти дает мне гораздо большую уверенность в том, что мой код верен, и это почти так же сложно, как издеваться над ORM.

SQLite - отличная база данных в памяти для модульного тестирования. Он находится в списке поддерживаемых PDO. (PDO - это драйвер базы данных Propel 1.3.) Если вы не хотите использовать базу данных в памяти, вы можете найти макет PDO, уже написанный.

0 голосов
/ 08 июля 2015

Это пример класса с жесткой зависимостью, который не может быть тестируемым модулем.

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

Лучшая альтернатива, о которой я думаю, - это иметь класс QueryFactory, который обернет все необходимые вам методы, а затем вы сможете его высмеять.

Сначала я создаю интерфейс

interface iQueryFactory
{
    function firstFunction($argument);
    function secondFunction($argument, $argument2);
}

QueryFactory со всеми вашими запросами ORM, которые нам нужны

class QueryFactory implements iQueryFactory
{
    function firstFunction($argument) 
    {
        // ORM thing
    }

    function secondFunction($argument, $argument2)
    {
        // ORM stuff
    }
}

Существует бизнес-логика с внедрением фабрики запросов

class BusinessLogic 
{
    protected $queryFactory;

    function __construct($queryFactoryInjection) 
    {
        $this->queryFactory= $queryFactoryInjection;
    }

    function yourFunctionInYourBusinessLogique($argument, $argument2) 
    {
        // business logique

        try {
            $this->queryFactory->secondFunction($argument, $argument2);
        } catch (\Exception $e) {
            // log
            // return thing
        }

        // return stuff
    }
}

Макет, обратите внимание, что я не использую макет фреймворка для моего примера (кстати, вы можете создать установщик ответа)

class QueryFactoryMock implements iQueryFactory
{
    function firstFunction($argument) 
    {
        if (is_null($argument)) 
        {
            throw new \Exception("");
        } 
        else 
        {
            return "succes";
        }
    }

    function firstFunction($argument, $argument2) 
    { 
        // sutff  
    }
}

Затем, наконец, юнит-тесты, которые проверяют нашу бизнес-логику с помощью фиктивной реализации

class BusinessLogicTest extends PHPUnit_Framework_TestCase 
{
    public function setUp() 
    {
        require_once "BusinessLogic.php";
    }

    public function testFirstFunction_WhenInsertGoodName() 
    {
        $queryMockup = new QueryFactoryMock();
        $businessLogicObject = new BusinessLogic($queryMockup);
        $response = $businessLogicObject ->firstFunction("fabien");

        $this->assertEquals($response, "succes");
    }

    public function testFirstFunction_WhenInsetNull() 
    {
        $queryMockup = new QueryFactoryMock();
        $businessLogicObject = new BusinessLogic($queryMockup);
        $response = $businessLogicObject->firstFunction(null);

        $this->assertEquals($response, "fail");
    }
}
0 голосов
/ 24 марта 2009

Я читал блог Миско Хевери о тестировании в последнее время. Это покрывает эту ситуацию; вам нужно использовать DI (внедрение зависимостей).

Я тоже немного борюсь с этим, и я также использую propel.

Например, вы можете переместить метод "suspend" в класс "Object", а не в одноранговый узел. В любом случае, для этой конкретной функции вам не нужно использовать статические методы для достижения этой цели. Ваш API может выглядеть так:

MyObjectPeer::retrieveByPK(1)->suspend();

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

Если действительно нужно проверить базу данных, то AFAIK, вам нужно задействовать базу данных в тесте. Я часто использую ltree и postgis в своем текущем проекте, и я не могу придумать какой-либо другой способ запуска модульных тестов для логики модели, который зависит от БД, кроме включения его в мои тесты.

...