【问题标题】:Zend Framework 2: How to include Doctrine2 support in unit testing with phpunit?Zend Framework 2:如何在 phpunit 的单元测试中包含 Doctrine2 支持?
【发布时间】:2014-07-19 10:58:26
【问题描述】:

经过进一步研究,我重新提出我的问题:我如何模拟 Doctrine2 的实体管理器进行单元测试,尤其是在控制器中? 基本上,我需要通过我的单元测试并替换任何相关的东西用 Doctrine2 的实体管理器模拟 TableGateway。


(原帖)

前段时间我浏览了官方的ZF2 2.1 入门教程并完成了它,然后又浏览了关于单元测试的附加教程。 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-&gt;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 模拟的基础知识,尽管我基于此的引用模拟了一个 FakeRepository() 对象,他们返回了未显示的 getRepository() 方法。另外,我不确定如何在测试中调用模拟的 EntityManager 来代替原始的 getMock 调用。

【问题讨论】:

  • 我不了解 Zend,但 Symfony 2 有一个可以扩展的 WebTestCase。然后,Web 测试用例使您可以访问依赖注入容器,您可以从中提取您的学说 2 实体管理器和存储库。 symfony.com/doc/current/cookbook/testing/doctrine.html。在 Zend 2 中寻找类似的东西。或者切换框架。

标签: php doctrine-orm zend-framework2 phpunit


【解决方案1】:

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

【讨论】:

    猜你喜欢
    • 2012-11-18
    • 2012-12-25
    • 1970-01-01
    • 1970-01-01
    • 2014-12-29
    • 2011-03-04
    • 2012-10-30
    • 2015-07-26
    • 2013-05-26
    相关资源
    最近更新 更多