Start Using symfony3 Beta1 Standard Edition
Symfony 3.0.0-BETA1 released last week, while we can wait for the installer for Symfony Standard Edition to support version 3, we can use it now via composer create project. Here we go, we can run in terminal the following command:
$ composer create-project symfony/framework-standard-edition sf3-std "v3.0.0-BETA1"
When installation complete, we can see the directory structure that slightly different then v2:
The structure for v3 is more like file system, where the executable file when old place is on app, now located in bin
directory, and there is var
directory to save cache, logs, and sessions.
Ok, now, let’s run server with console command:
$ php bin/console server:run
And we can see the following:
We can call 127.0.0.1:8000 in the browser, then:
Very cool! Didn’t try it? You actually need to give a try!
References:
Testing Zend Framework 2 application using phpspec
If you’re going to start working with new ZF2 application, it is be a good chance to use phpspec for testing tool. We can describe specification and generate code that we already describe. Ok, let’s start with clone ZF2 skeleton application:
$ composer create-project zendframework/skeleton-application:dev-master zfnew
We will start application with “test first”, so, we can remove current module/Application’s classes:
$ cd zfnew $ rm -rf module/Application/Module.php $ rm -rf module/Application/src/Application/Controller/IndexController.php
The next step is setup requiring phpspec dependency and its needed extensions:
$ composer config bin-dir bin $ composer require phpspec/phpspec:~2.3.0 \ henrikbjorn/phpspec-code-coverage:~1.0.1 \ ciaranmcnulty/phpspec-typehintedmethods:~1.1 --dev
We will use henrikbjorn/phpspec-code-coverage
for code coverage generation, and ciaranmcnulty/phpspec-typehintedmethods
for typehint generation when running phpspec.
By default, our Application
module follow PSR-0 autoloader, so we need to define it in composer.json
:
// ... "autoload": { "psr-0": { "Application\\": "module/Application/src/" } }, // ...
To make it registered in composer’s autoload, we need to run dump-autoload:
$ composer dump-autoload
To point spec to describe Application namespace inside module/Application/src, we need to setup phpspec config under phpspec.yml, we can place it in root zfnew project:
# zfnew/phpspec.yml suites: application_suite: namespace: Application src_path: module/Application/src/ spec_path: module/Application extensions: - PhpSpec\Extension\CodeCoverageExtension - Cjm\PhpSpec\Extension\TypeHintedMethodsExtension code_coverage: format: - html - clover whitelist: - module/Application/src output: html: coverage clover: build/logs/clover.xml
Ok, let’s generate our first spec:
$ bin/phpspec desc Application/Module
We will get output like the following:
We will get generated first spec like the following:
// module/Application/spec/Application/ModuleSpec.php namespace spec\Application; use PhpSpec\ObjectBehavior; use Prophecy\Argument; class ModuleSpec extends ObjectBehavior { function it_is_initializable() { $this->shouldHaveType('Application\Module'); } }
And when we run:
$ bin/phpspec run
We will get generated class like this if we choose ‘Y’ answering “Do you want me to create Application\Module
for you?” question:
And we will get a Module class inside module/Application/src/Application
directory:
namespace Application; class Module { }
We need to have more examples to achieve standard Module class, that has getConfig()
method, and especially for Application
module, we need onBootstrap(MvcEvent $e)
method, so we can write examples like the following:
// module/Application/spec/Application/ModuleSpec.php namespace spec\Application; use Application\Module; use PhpSpec\ObjectBehavior; use Zend\EventManager\EventManager; use Zend\Mvc\Application; use Zend\Mvc\MvcEvent; class ModuleSpec extends ObjectBehavior { function it_is_initializable() { $this->shouldHaveType(Module::class); } function it_return_config() { $getConfig = $this->getConfig(); $getConfig->shouldBeArray(); $getConfig->shouldReturn( include __DIR__ . '/../../config/module.config.php' ); } function its_bootstrap(MvcEvent $e, $application, $eventManager) { $application->beADoubleOf(Application::class); $eventManager->beADoubleOf(EventManager::class); $application->getEventManager()->willReturn($eventManager)->shouldBeCalled(); $e->getApplication()->willReturn($application)->shouldBeCalled(); $this->onBootstrap($e); } }
And when run, we will get the following errors:
Do you want me to create `Application\Module::getConfig()` for you? [Y/n] Y Method Application\Module::getConfig() has been created. Do you want me to create `Application\Module::onBootstrap()` for you? [Y/n] Y Method Application\Module::onBootstrap() has been created. Application/Module 18 - it return config is_array(null) expected to return true, but it did not. Application/Module 28 - its bootstrap some predictions failed: Double\Zend\Mvc\MvcEvent\P2: No calls have been made that match: Double\Zend\Mvc\MvcEvent\P2->getApplication() but expected at least one. Double\Zend\Mvc\Application\P1: No calls have been made that match: Double\Zend\Mvc\Application\P1->getEventManager() but expected at least one. 33% 66% 3 1 specs 3 examples (1 passed, 2 failed) 708ms
Don’t worry about it, it is normal, we just need to fulfill what already described in code as we have generated code template:
namespace Application; class Module { public function getConfig() { // TODO: write logic here } public function onBootstrap(\Zend\Mvc\MvcEvent $mvcEvent) { // TODO: write logic here } }
let’s fill it so it looks like:
namespace Application; use Zend\Mvc\ModuleRouteListener; class Module { public function getConfig() { return include __DIR__ . '/../../config/module.config.php'; } public function onBootstrap(\Zend\Mvc\MvcEvent $mvcEvent) { $eventManager = $mvcEvent->getApplication()->getEventManager(); $moduleRouteListener = new ModuleRouteListener(); $moduleRouteListener->attach($eventManager); } }
To prove, you can re-run bin/phpspec run
and everything will be green ;).
Now, let’s create spec for Application\Controller\IndexController
:
$ bin/phpspec desc Application/Controller/IndexController
And we can define the IndexControllerSpec:
// module/Application/spec/Application/Controller/IndexControllerSpec.php namespace spec\Application\Controller; use Application\Controller\IndexController; use PhpSpec\ObjectBehavior; use Zend\Mvc\Controller\AbstractActionController; use Zend\View\Model\ViewModel; class IndexControllerSpec extends ObjectBehavior { function let(ViewModel $viewModel) { $this->beConstructedWith($viewModel); } function it_is_initializable() { $this->shouldHaveType(IndexController::class); } function it_is_extends_abstract_action_controller() { $this->shouldBeAnInstanceOf(AbstractActionController::class); } function its_index_action_return_view_model(ViewModel $viewModel) { $this->indexAction()->shouldReturn($viewModel); } }
We use beConstructedWith()
, so, we need to inject ViewModel into controller’s construction. We can run bin/phpspec run
and we will get the following code:
// module/Application/src/Application/Controller/IndexController.php namespace Application\Controller; class IndexController { public function __construct(\Zend\View\Model\ViewModel $viewModel) { // TODO: write logic here } public function indexAction() { // TODO: write logic here } }
Let’s fulfill the examples as described in spec:
// module/Application/src/Application/Controller/IndexController.php namespace Application\Controller; use Zend\Mvc\Controller\AbstractActionController; class IndexController extends AbstractActionController { private $viewModel; public function __construct(\Zend\View\Model\ViewModel $viewModel) { $this->viewModel = $viewModel; } public function indexAction() { return $this->viewModel; } }
We need to build the Controller with Factory, so we can describe the factory:
$ bin/phpspec desc Application/Factory/Controller/IndexControllerFactory
We can write spec examples:
// module/Application/spec/Application/Factory/Controller/IndexControllerFactorySpec.php namespace spec\Application\Factory\Controller; use Application\Controller\IndexController; use Application\Factory\Controller\IndexControllerFactory; use PhpSpec\ObjectBehavior; use Zend\ServiceManager\FactoryInterface; use Zend\ServiceManager\ServiceLocatorInterface; class IndexControllerFactorySpec extends ObjectBehavior { function it_is_initializable() { $this->shouldHaveType(IndexControllerFactory::class); } function it_is_implements_factory_interface() { $this->shouldImplement(FactoryInterface::class); } function it_is_create_indexcontroller(ServiceLocatorInterface $serviceLocator) { $this->createService($serviceLocator) ->shouldReturnAnInstanceOf(IndexController::class); } }
When run bin/phpspec run
, we will get generated code:
// module/Application/src/Application/Factory/Controller/IndexControllerFactory.php namespace Application\Factory\Controller; class IndexControllerFactory { public function createService(\Zend\ServiceManager\ServiceLocatorInterface $serviceLocatorInterface) { // TODO: write logic here } }
Let’s modify to fulfill the spec examples:
// module/Application/src/Application/Factory/Controller/IndexControllerFactory.php namespace Application\Factory\Controller; use Application\Controller\IndexController; use Zend\ServiceManager\FactoryInterface; use Zend\View\Model\ViewModel; class IndexControllerFactory implements FactoryInterface { public function createService(\Zend\ServiceManager\ServiceLocatorInterface $serviceLocatorInterface) { $viewModel = new ViewModel(); return new IndexController($viewModel); } }
So, everything looks good, we can run bin/phpspec run
again, and we can get green result again:
We can see the coverage result in coverage/index.html
:
Now, to make our ZF2 application still works when call ‘/’ in browser, we can update our module/Application/config/module.config.php
:
// ... 'controllers' => array( 'factories' => array( 'Application\Controller\Index' => 'Application\Factory\Controller\IndexControllerFactory' ), ), // ...
That’s it 😉
leave a comment