【发布时间】: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->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