Zend Framework 2:如何在phpunit的单元测试中包含Doctrine2支持?

时间:2014-05-29 19:51:36

标签: php doctrine-orm zend-framework2 phpunit

经过进一步的研究,我正在重述我的问题:如何模拟Doctrine2的实体管理器进行单元测试,特别是在控制器中?基本上,我需要经过单元测试并更换任何相关内容使用Doctrine2的实体管理器模拟TableGateway。


(原帖)

前段时间我通过了2.1版的官方ZF2入门教程并完成了它,然后完成了有关单元测试的附加教程。 http://framework.zend.com/manual/2.1/en/user-guide/overview.html http://framework.zend.com/manual/2.1/en/tutorials/unittesting.html

完成这些教程之后,我得出结论,Zend_Db不太可能优雅地处理我的应用程序需求,因此开始考虑将Doctrine 2集成并使用到Zend Framework 2.我很难找到好的示例,然后找到了以下教程这是一个好的开始。 http://www.jasongrimes.org/2012/01/using-doctrine-2-in-zend-framework-2/

正是在这里我被挂断了,因为我想将Doctrine 2的更改纳入我的单元测试,但无法弄清楚如何。经过多年毫无结果的研究,我把这个项目放了一段时间,现在回到它。我找到了关于这个主题的另一个体面的教程,但它涉及ZF1,所以结构明显不同。 http://www.zendcasts.com/unit-testing-doctrine-2-entities/2011/02/

然而,有了新鲜的眼光并将我的示例ZF2文件与与ZF1框架相关联的文件进行比较,我至少看到我可能需要在phpunit Bootstrap文件中设置Doctrine实体管理器然后调用它控制器测试正确。

目前,当我运行phpunit时,我收到以下错误:

There was 1 failure:

1) AlbumTest\Controller\AlbumControllerTest::testEditActionRedirectsAfterValidPost
Expectation failed for method name is equal to <string:find> when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.

在这个测试方法的相关部分,我目前有:

    //$albumTableMock = $this->getMockBuilder('Album\Models\AlbumTable')  // Zend_Db
    $albumTableMock = $this->getMockBuilder('Album\Entity\Album')
                           ->disableOriginalConstructor()
                           ->getMock();

(我真的不确定'Album \ Entity \ Album'在这里是否正确但我的理解是因为Doctrine 2,我不再使用'Album \ Models \ Album'或'Album \ Models \我在初始教程中创建的AlbumTable。)

在AlbumController中,相关的代码部分位于:

    // Get the Album with the specified id.  An exception is thrown
    // if it cannot be found, in which case go to the index page.
    try {
        //$album = $this-> getAlbumTable()->getAlbum($id); // Zend_DB
        $album = $this->getEntityManager()->find('Album\Entity\Album', $id); // Doctrine
    }

所以我的主要问题是如何设置我的phpunit Bootstrap文件以包含Doctrine2?


(附录) 我现在回过头来讨论上面的场景,但是目前我正在尝试成功模拟我的测试的实体管理器,并且我已经在我的控制器测试中注释掉了上面的getMocks以获得基础知识。我已经为Album \ Entity \ Album设置了一个基本的测试,所以我认为我的引导是可以的,但我现在失败,试图在使用$this->dispatch()时试图从控制器访问数据库,我不知道我想要。

PHPUnit失败:

1) AlbumTest\Controller\AlbumControllerTest::testIndexActionCanBeAccessed
PDOException: SQLSTATE[HY000] [1045] Access denied for user 'username'@'localhost' (using password: YES)

我没有在任何地方明确定义此用户,所以我猜测没有找到或加载配置文件,但我还想模拟数据库连接。

这是我的测试引导程序:

<?php

namespace AlbumTest;

use Zend\Loader\AutoloaderFactory;
use Zend\Mvc\Service\ServiceManagerConfig;
use Zend\ServiceManager\ServiceManager;
//use DoctrineORMModuleTest\Framework\TestCase;
//use Doctrine\Tests\Mocks;
use RuntimeException;

error_reporting(E_ALL | E_STRICT);
chdir(__DIR__);

// Set timezone or PHPUnit reports error
date_default_timezone_set('America/Chicago');

/**
 * Test bootstrap, for setting up autoloading
 */
class Bootstrap
{
    protected static $serviceManager;
    protected static $em; // Doctrine


    public static function init()
    {
        $zf2ModulePaths = array(dirname(dirname(__DIR__)));
        if (($path = static::findParentPath('vendor'))) {
            $zf2ModulePaths[] = $path;
        }
        if (($path = static::findParentPath('module')) !== $zf2ModulePaths[0]) {
            $zf2ModulePaths[] = $path;
        }

        static::initAutoloader();

        // use ModuleManager to load this module and it's dependencies
        $config = array(
            'module_listener_options' => array(
                'module_paths' => $zf2ModulePaths,
            ),
            'modules' => array(
                'DoctrineModule',
                'DoctrineORMModule',
                'Album'
            )
        );

        $serviceManager = new ServiceManager(new ServiceManagerConfig());
        $serviceManager->setService('ApplicationConfig', $config);
        $serviceManager->get('ModuleManager')->loadModules();
        static::$serviceManager = $serviceManager;
        static::$em = self::getEntityManager($serviceManager); // Doctrine
    }


    public static function getServiceManager()
    {
        return static::$serviceManager;
    }


    public static function getEntityManager($serviceManager)
    {
        return $serviceManager->get('Doctrine\ORM\EntityManager');
    }


    protected static function initAutoloader()
    {
        $vendorPath = static::findParentPath('vendor');

        $zf2Path = getenv('ZF2_PATH');
        if (!$zf2Path) {
            if (defined('ZF2_PATH')) {
                $zf2Path = ZF2_PATH;
            } else {
                if (is_dir($vendorPath . '/ZF2/library')) {
                    $zf2Path = $vendorPath . '/ZF2/library';
                }
            }
        }

        if (!$zf2Path) {
            throw new RuntimeException('Unable to load ZF2. Run `php composer.phar install` or define a ZF2_PATH environment variable.');
        }

        include $zf2Path . '/Zend/Loader/AutoloaderFactory.php';
        AutoloaderFactory::factory(array(
            'Zend\Loader\StandardAutoloader' => array(
                'autoregister_zf' => true,
                'namespaces' => array(
                    __NAMESPACE__ => __DIR__ . '/' . __NAMESPACE__,
                ),
            ),
        ));
    }


    protected static function findParentPath($path)
    {
        // This function traverses up from the working directory
        // until it finds the parent of the named path
        // (used to find 'vendor' parent in initAutoloader).
        $dir = __DIR__;
        $previousDir = '.';
        while (!is_dir($dir . '/' . $path)) {
            $dir = dirname($dir);
            if ($previousDir === $dir) return false;
            $previousDir = $dir;
        }
        return $dir . '/' . $path;
    }
}

Bootstrap::init();

这是我的实体测试(希望证明Doctrine是正确引导的):

<?php

namespace AlbumTest\Entity;

use Album\Entity\Album;
use PHPUnit_Framework_TestCase;


class AlbumTest extends PHPUnit_Framework_TestCase
{
    public function testAlbumInitialState() {

        $album = new Album();

        $this->assertNull($album->artist, '"artist" should initially be null');
        $this->assertNull($album->id, '"id" should initially be null');
        $this->assertNull($album->title, '"title" should initially be null');
    }
}

以下是我的控制器测试的相关部分:

<?php

namespace AlbumTest\Controller;

//use Album\Models\Album;
use Album\Entity\Album;
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
use Zend\Db\ResultSet\ResultSet;
//use PHPUnit_Framework_TestCase;


class AlbumControllerTest extends AbstractHttpControllerTestCase
{
    protected $traceError = true;

    public function setUp()
    {
        // Need to mock entity manager for doctrine use
        $this->em = $this->getMock('EntityManager', array('persist', 'flush', 'find','getClassMetaData','getRepository'));
        $this->em
            ->expects($this->any())
            ->method('persist')
            ->will($this->returnValue(true));
        $this->em
            ->expects($this->any())
            ->method('flush')
            ->will($this->returnValue(true));
        $this->em
            ->expects($this->any())
            ->method('find')
            ->will($this->returnValue(true));
        $this->em
            ->expects($this->any())
            ->method('getRepository')
            ->will($this->returnValue(true));
        $this->em
            ->expects($this->any())
            ->method('getClassMetadata')
            ->will($this->returnValue((object)array('name' => 'aClass')));
        $this->doctrine = $this->getMock('Doctrine', array('getEntityManager'));
        $this->doctrine
            ->expects($this->any())
            ->method('getEntityManager')
            ->will($this->returnValue($this->em));

        $this->setApplicationConfig(
            include __DIR__ . '../../../../../../config/application.config.php'
        );
        parent::setUp();
    }


    public function testIndexActionCanBeAccessed()
    {
        //$albumTableMock = $this->getMockBuilder('Album\Models\AlbumTable')
        $albumTableMock = $this->getMockBuilder('Album\Entity\Album')
                               ->disableOriginalConstructor()
                               ->getMock();

        $albumTableMock->expects($this->once())
                       //->method('fetchAll')
                       ->method('findAll')
                       ->will($this->returnValue(array()));

        $serviceManager = $this->getApplicationServiceLocator();
        $serviceManager->setAllowOverride(true);
        $serviceManager->setService('Album\Entity\Album', $albumTableMock);


        $this->dispatch('/album');
        $this->assertResponseStatusCode(200);

        $this->assertModuleName('Album');
        $this->assertControllerName('Album\Controller\Album');
        $this->assertControllerClass('AlbumController');
        $this->assertMatchedRouteName('album');
    }

我认为我已经设置了EntityManager mock的基础知识,尽管我基于此的引用已经模拟了一个FakeRepository()对象,它们返回了未显示的getRepository()方法。另外,我不确定如何在测试中调用模拟的EntityManager来代替原始的getMock调用。

1 个答案:

答案 0 :(得分:0)

您需要将GenericTestsDatabaseTestCase用于数据库测试用例,并且需要以xml格式提供mysql转储文件作为数据库测试用例类中的数据集。因此,在单元测试期间,应用程序不会查询实际的数据库,而是查询mysql xml dump。如果没有任何匹配的列等,则在教义实体中的异常/错误等其他内容将按原样工作。简而言之,由于数据集[数据库xml转储文件],doctine将在xml数据集文件而不是实际数据库上触发查询。

相关问题