经过进一步的研究,我正在重述我的问题:如何模拟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调用。
答案 0 :(得分:0)
您需要将GenericTestsDatabaseTestCase用于数据库测试用例,并且需要以xml格式提供mysql转储文件作为数据库测试用例类中的数据集。因此,在单元测试期间,应用程序不会查询实际的数据库,而是查询mysql xml dump。如果没有任何匹配的列等,则在教义实体中的异常/错误等其他内容将按原样工作。简而言之,由于数据集[数据库xml转储文件],doctine将在xml数据集文件而不是实际数据库上触发查询。