ZF2 教程专辑 PHPUnit 测试失败,未定义属性:专辑测试模型专辑表测试::$controller


ZF2 tutorial Album PHPUnit test failing with Undefined property: AlbumTestModelAlbumTableTest::$controller

我正在构建 ZF2 相册教程应用程序,当我进行单元测试时,即使我再次重建了应用程序,我也始终收到相同的错误。 有人可以告诉我这里发生了什么吗? 我在这里倾倒所有相关信息以提供帮助。 错误是:

PHPUnit 3.7.10 by Sebastian Bergmann.
Configuration read from D:'PHP'zf2-tutorial'module'Album'test'phpunit.xml.dist
......E
Time: 0 seconds, Memory: 6.25Mb
There was 1 error:
1) AlbumTest'Model'AlbumTableTest::testGetAlbumTableReturnsAnInstanceOfAlbumTable
Undefined property: AlbumTest'Model'AlbumTableTest::$controller
D:'PHP'zf2-tutorial'module'Album'test'AlbumTest'Model'AlbumTableTest.php:116
FAILURES!
Tests: 7, Assertions: 9, Errors: 1.

AlbumTableTest.php 如下所示,在最终断言时收到错误:

<?php
namespace AlbumTest'Model;
use Album'Model'AlbumTable;
use Album'Model'Album;
use Zend'Db'ResultSet'ResultSet;
use PHPUnit_Framework_TestCase;
class AlbumTableTest extends PHPUnit_Framework_TestCase {
    public function testFetchAllReturnsAllAlbums() {
        $resultSet = new ResultSet();
        $mockTableGateway = $this->getMock('Zend'Db'TableGateway'TableGateway', array('select'), array(), '', false);
        $mockTableGateway->expects($this->once())
                ->method('select')
                ->with()
                ->will($this->returnValue($resultSet));
        $albumTable = new AlbumTable($mockTableGateway);
        $this->assertSame($resultSet, $albumTable->fetchAll());
    }
    public function testCanRetrieveAnAlbumByItsId() {
        $album = new Album();
        $album->exchangeArray(array('id' => 123,
            'artist' => 'The Military Wives',
            'title' => 'In My Dreams'));
        $resultSet = new ResultSet();
        $resultSet->setArrayObjectPrototype(new Album());
        $resultSet->initialize(array($album));
        $mockTableGateway = $this->getMock('Zend'Db'TableGateway'TableGateway', array('select'), array(), '', false);
        $mockTableGateway->expects($this->once())
                ->method('select')
                ->with(array('id' => 123))
                ->will($this->returnValue($resultSet));
        $albumTable = new AlbumTable($mockTableGateway);
        $this->assertSame($album, $albumTable->getAlbum(123));
    }
    public function testCanDeleteAnAlbumByItsId() {
        $mockTableGateway = $this->getMock('Zend'Db'TableGateway'TableGateway', array('delete'), array(), '', false);
        $mockTableGateway->expects($this->once())
                ->method('delete')
                ->with(array('id' => 123));
        $albumTable = new AlbumTable($mockTableGateway);
        $albumTable->deleteAlbum(123);
    }
    public function testSaveAlbumWillInsertNewAlbumsIfTheyDontAlreadyHaveAnId() {
        $albumData = array('artist' => 'The Military Wives', 'title' => 'In My Dreams');
        $album = new Album();
        $album->exchangeArray($albumData);
        $mockTableGateway = $this->getMock('Zend'Db'TableGateway'TableGateway', array('insert'), array(), '', false);
        $mockTableGateway->expects($this->once())
                ->method('insert')
                ->with($albumData);
        $albumTable = new AlbumTable($mockTableGateway);
        $albumTable->saveAlbum($album);
    }
    public function testSaveAlbumWillUpdateExistingAlbumsIfTheyAlreadyHaveAnId() {
        $albumData = array('id' => 123, 'artist' => 'The Military Wives', 'title' => 'In My Dreams');
        $album = new Album();
        $album->exchangeArray($albumData);
        $resultSet = new ResultSet();
        $resultSet->setArrayObjectPrototype(new Album());
        $resultSet->initialize(array($album));
        $mockTableGateway = $this->getMock('Zend'Db'TableGateway'TableGateway', array('select', 'update'), array(), '', false);
        $mockTableGateway->expects($this->once())
                ->method('select')
                ->with(array('id' => 123))
                ->will($this->returnValue($resultSet));
        $mockTableGateway->expects($this->once())
                ->method('update')
                ->with(array('artist' => 'The Military Wives', 'title' => 'In My Dreams'), array('id' => 123));
        $albumTable = new AlbumTable($mockTableGateway);
        $albumTable->saveAlbum($album);
    }
    public function testExceptionIsThrownWhenGettingNonexistentAlbum() {
        $resultSet = new ResultSet();
        $resultSet->setArrayObjectPrototype(new Album());
        $resultSet->initialize(array());
        $mockTableGateway = $this->getMock('Zend'Db'TableGateway'TableGateway', array('select'), array(), '', false);
        $mockTableGateway->expects($this->once())
                ->method('select')
                ->with(array('id' => 123))
                ->will($this->returnValue($resultSet));
        $albumTable = new AlbumTable($mockTableGateway);
        try {
            $albumTable->getAlbum(123);
        } catch ('Exception $e) {
            $this->assertSame('Could not find row 123', $e->getMessage());
            return;
        }
        $this->fail('Expected exception was not thrown');
    }
    public function testGetAlbumTableReturnsAnInstanceOfAlbumTable() {
        $this->assertInstanceOf('Album'Model'AlbumTable', $this->controller->getAlbumTable());
    }
}
?>

getAlbumTable 位于 AlbumController 中,如下所示:

<?php
namespace Album'Controller;
use Zend'Mvc'Controller'AbstractActionController;
use Zend'View'Model'ViewModel;
class AlbumController extends AbstractActionController {
    protected $albumTable;
    public function indexAction() {
        return new ViewModel(array(
                    'albums' => $this->getAlbumTable()->fetchAll(),
                ));
    }
    public function addAction() {
    }
    public function editAction() {
    }
    public function deleteAction() {
    }
    public function getAlbumTable() {
        if (!$this->albumTable) {
            $sm = $this->getServiceLocator();
            $this->albumTable = $sm->get('Album'Model'AlbumTable');
        }
        return $this->albumTable;
    }
}
?>

而专辑表是:

<?php
namespace Album'Model;
use Zend'Db'TableGateway'TableGateway;
class AlbumTable
{
    protected $tableGateway;
    public function __construct(TableGateway $tableGateway)
    {
        $this->tableGateway = $tableGateway;
    }
    public function fetchAll()
    {
        $resultSet = $this->tableGateway->select();
        return $resultSet;
    }
    public function getAlbum($id)
    {
        $id  = (int) $id;
        $rowset = $this->tableGateway->select(array('id' => $id));
        $row = $rowset->current();
        if (!$row) {
            throw new 'Exception("Could not find row $id");
        }
        return $row;
    }
    public function saveAlbum(Album $album)
    {
        $data = array(
            'artist' => $album->artist,
            'title'  => $album->title,
        );
        $id = (int)$album->id;
        if ($id == 0) {
            $this->tableGateway->insert($data);
        } else {
            if ($this->getAlbum($id)) {
                $this->tableGateway->update($data, array('id' => $id));
            } else {
                throw new 'Exception('Form id does not exist');
            }
        }
    }
    public function deleteAlbum($id)
    {
        $this->tableGateway->delete(array('id' => $id));
    }
}
?>

模块.php为:

<?php
namespace Album;
class Module
{
    public function getAutoloaderConfig()
    {
        return array(
            'Zend'Loader'ClassMapAutoloader' => array(
                __DIR__ . '/autoload_classmap.php',
            ),
            'Zend'Loader'StandardAutoloader' => array(
                'namespaces' => array(
                    __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
                ),
            ),
        );
    }
    public function getConfig()
    {
        return include __DIR__ . '/config/module.config.php';
    }
    // Add this method:
    public function getServiceConfig()
    {
        return array(
            'factories' => array(
                'Album'Model'AlbumTable' =>  function($sm) {
                    $tableGateway = $sm->get('AlbumTableGateway');
                    $table = new AlbumTable($tableGateway);
                    return $table;
                },
                'AlbumTableGateway' => function ($sm) {
                    $dbAdapter = $sm->get('Zend'Db'Adapter'Adapter');
                    $resultSetPrototype = new ResultSet();
                    $resultSetPrototype->setArrayObjectPrototype(new Album());
                    return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
                },
            ),
        );
    }
}
?>

module.config.php 是:

<?php
return array(
    'controllers' => array(
        'invokables' => array(
            'Album'Controller'Album' => 'Album'Controller'AlbumController',
        ),
    ),
    // The following section is new and should be added to your file
    'router' => array(
        'routes' => array(
            'album' => array(
                'type'    => 'segment',
                'options' => array(
                    'route'    => '/album[/:action][/:id]',
                    'constraints' => array(
                        'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
                        'id'     => '[0-9]+',
                    ),
                    'defaults' => array(
                        'controller' => 'Album'Controller'Album',
                        'action'     => 'index',
                    ),
                ),
            ),
        ),
    ),
    'view_manager' => array(
        'template_path_stack' => array(
            'album' => __DIR__ . '/../view',
        ),
    ),
);
?>

application.config.php 是:

<?php
return array(
    'modules' => array(
        'Application',
        'Album',                  // <-- Add this line
    ),
    'module_listener_options' => array(
        'config_glob_paths'    => array(
            'config/autoload/{,*.}{global,local}.php',
        ),
        'module_paths' => array(
            './module',
            './vendor',
        ),
    ),
);
?>

引导.php:

<?php
namespace AlbumTest;//Change this namespace for your test
use Zend'Loader'AutoloaderFactory;
use Zend'Mvc'Service'ServiceManagerConfig;
use Zend'ServiceManager'ServiceManager;
use Zend'Stdlib'ArrayUtils;
use RuntimeException;
error_reporting(E_ALL | E_STRICT);
chdir(__DIR__);
class Bootstrap
{
    protected static $serviceManager;
    protected static $config;
    protected static $bootstrap;
    public static function init()
    {
        // Load the user-defined test configuration file, if it exists; otherwise, load
        if (is_readable(__DIR__ . '/TestConfig.php')) {
            $testConfig = include __DIR__ . '/TestConfig.php';
        } else {
            $testConfig = include __DIR__ . '/TestConfig.php.dist';
        }
        $zf2ModulePaths = array();
        if (isset($testConfig['module_listener_options']['module_paths'])) {
            $modulePaths = $testConfig['module_listener_options']['module_paths'];
            foreach ($modulePaths as $modulePath) {
                if (($path = static::findParentPath($modulePath)) ) {
                    $zf2ModulePaths[] = $path;
                }
            }
        }
        $zf2ModulePaths  = implode(PATH_SEPARATOR, $zf2ModulePaths) . PATH_SEPARATOR;
        $zf2ModulePaths .= getenv('ZF2_MODULES_TEST_PATHS') ?: (defined('ZF2_MODULES_TEST_PATHS') ? ZF2_MODULES_TEST_PATHS : '');
        static::initAutoloader();
        // use ModuleManager to load this module and it's dependencies
        $baseConfig = array(
            'module_listener_options' => array(
                'module_paths' => explode(PATH_SEPARATOR, $zf2ModulePaths),
            ),
        );
        $config = ArrayUtils::merge($baseConfig, $testConfig);
        $serviceManager = new ServiceManager(new ServiceManagerConfig());
        $serviceManager->setService('ApplicationConfig', $config);
        $serviceManager->get('ModuleManager')->loadModules();
        static::$serviceManager = $serviceManager;
        static::$config = $config;
    }
    public static function getServiceManager()
    {
        return static::$serviceManager;
    }
    public static function getConfig()
    {
        return static::$config;
    }
    protected static function initAutoloader()
    {
        $vendorPath = static::findParentPath('vendor');
        if (is_readable($vendorPath . '/autoload.php')) {
            $loader = include $vendorPath . '/autoload.php';
        } else {
            $zf2Path = getenv('ZF2_PATH') ?: (defined('ZF2_PATH') ? ZF2_PATH : (is_dir($vendorPath . '/ZF2/library') ? $vendorPath . '/ZF2/library' : false));
            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)
    {
        $dir = __DIR__;
        $previousDir = '.';
        while (!is_dir($dir . '/' . $path)) {
            $dir = dirname($dir);
            if ($previousDir === $dir) return false;
            $previousDir = $dir;
        }
        return $dir . '/' . $path;
    }
}
Bootstrap::init();
?>

TestConfig.php.dist:

<?php
return array(
    'modules' => array(
        'Album',
    ),
    'module_listener_options' => array(
        'config_glob_paths'    => array(
            '../../../config/autoload/{,*.}{global,local}.php',
        ),
        'module_paths' => array(
            'module',
            'vendor',
        ),
    ),
);
?>

最后,phpunit.xml.dist:

<?xml version="1.0" encoding="UTF-8"?> 
<phpunit bootstrap="Bootstrap.php"> 
    <testsuites> 
        <testsuite name="zf2tutorial"> 
            <directory>./AlbumTest</directory> 
        </testsuite> 
    </testsuites> 
</phpunit>

您正确地假设您需要添加一些设置代码并将其与AlbumControllerTest.php进行比较是个好主意。

如错误消息所述,问题是AlbumTableTest对象没有controller属性。 因此,我们需要使用以下方法添加属性:

$protected controller

并使用以下方法对其进行初始化:

$this->controller = new AlbumController()

此外,我们需要初始化 serviceManager 属性并设置其 serviceLocator 属性,以便以下调用在控制器的 getAlbumTable 方法中有意义:

$sm = $this->getServiceLocator();
$this->albumTable = $sm->get('Album'Model'AlbumTable');

总而言之,你做了正确的事情,但你不需要所有额外的代码。 您可以侥幸逃脱:

use AlbumTest'Bootstrap;
use Album'Controller'AlbumController;
use Album'Model'AlbumTable;
use Album'Model'Album;
use Zend'Db'ResultSet'ResultSet;
use PHPUnit_Framework_TestCase; 

class AlbumTableTest extends PHPUnit_Framework_TestCase       
{                                                             
protected $controller; 

    protected function setUp()
    {   
        $serviceManager = Bootstrap::getServiceManager();
        $this->controller = new AlbumController();  
        $this->controller->setServiceLocator($serviceManager);
    }
....

AlbumTableTest.php开始时

虽然我不确定此时这是否是适当的修复程序,但我解决了错误。 我研究了AlbumControllerTest而不是AlbumTableTest。 它具有创建控制器类的设置方法。 我复制了设置代码以及适当的 use 语句和变量声明,目前,我正在下一个问题......

我仍然对更好的答案感兴趣!

我复制(添加(的内容包括:

use AlbumTest'Bootstrap;
use Album'Controller'AlbumController;
use Zend'Http'Request;
use Zend'Http'Response;
use Zend'Mvc'MvcEvent;
use Zend'Mvc'Router'RouteMatch;
use Zend'Mvc'Router'Http'TreeRouteStack as HttpRouter;
use PHPUnit_Framework_TestCase;

class AlbumTableTest extends PHPUnit_Framework_TestCase {
    protected $controller;
    protected $request;
    protected $response;
    protected $routeMatch;
    protected $event;
    protected function setUp()
    {
        $serviceManager = Bootstrap::getServiceManager();
        $this->controller = new AlbumController();
        $this->request    = new Request();
        $this->routeMatch = new RouteMatch(array('controller' => 'index'));
        $this->event      = new MvcEvent();
        $config = $serviceManager->get('Config');
        $routerConfig = isset($config['router']) ? $config['router'] : array();
        $router = HttpRouter::factory($routerConfig);
        $this->event->setRouter($router);
        $this->event->setRouteMatch($this->routeMatch);
        $this->controller->setEvent($this->event);
        $this->controller->setServiceLocator($serviceManager);
    }

您忘了在模块.php中添加新的导入语句。文件的开头应如下所示:

namespace Album;
use Album'Model'Album;
use Album'Model'AlbumTable;
use Zend'Db'ResultSet'ResultSet;
use Zend'Db'TableGateway'TableGateway;
class Module
{
 //...

容易跳过,很难找到,我在同一件事上花了 15 分钟......