Zend Framework not handing camel case controller properly in unit testing - unit-testing

I'm working on my first ZF project and am a bit stuck. I have a controller named 'WebServiceController' and I have a unit test for the WebServiceController but when I run the test it doesn't assert the controller properly. My test code is:
public function testIndexAction() {
$params = array('action' => 'index', 'controller' => 'WebService', 'module' => 'default');
$url = $this->url($this->urlizeOptions($params));
$this->dispatch($url);
// assertions
$this->assertModule($params['module']);
$this->assertController($params['controller']);
$this->assertAction($params['action']);
$this->assertQueryContentContains(
'div#view-content p',
'View script for controller <b>' . $params['controller'] . '</b> and script/action name <b>' . $params['action'] . '</b>'
);
}
The error I get is:
1) WebServiceControllerTest::testIndexAction
Failed asserting last controller used <"Web-Service"> was "WebService"
It looks like its trying to assert the controller was 'Web-Service' instead of 'WebService'. Can anyone point me in the right direction?
Thanks in advance.
Ziad
p.s. I am using ZF 1.11.5

In Zend Framework a camel case name for controller is rewritten in the URL :
WebService in the class name becomes web-service in the URL.
This is a normal behavior, see http://framework.zend.com/manual/1.12/en/zend.controller.basics.html :
Since humans are notoriously
inconsistent at maintaining case
sensitivity when typing links, Zend
Framework actually normalizes path
information to lowercase. This, of
course, will affect how you name your
controller and actions... or refer to
them in links. If you wish to have
your controller class or action method
name have multiple MixedCasedWords or
camelCasedWords, you will need to
separate those words on the url with
either a '-' or '.' (though you can
configure the character used). As an
example, if you were going to the
action in
FooBarController::bazBatAction(),
you'd refer to it on the url as
/foo-bar/baz-bat or /foo.bar/baz.bat.

Related

Get name of next doctrine migration

How could I get the name / version of the next migration to execute? Something similar to migrations:latest but more like migrations:next. I need this as input to another command so it needs to be parseable output (can't really just use migrations:status).
You can use the Configuration object of the Doctrine migrations bundle. This is even (somewhat) documented as custom configuration.
Here is a minimal code example that works for me:
public function migrationVersionAction(EntityManagerInterface $em, ParameterBagInterface $parameters) {
$connection = $em->getConnection();
$configuration = new \Doctrine\Migrations\Configuration\Configuration($connection);
$configuration->setMigrationsNamespace($parameters->get('doctrine_migrations.namespace'));
$configuration->setMigrationsDirectory($parameters->get('doctrine_migrations.dir_name'));
$configuration->setMigrationsTableName($parameters->get('doctrine_migrations.table_name'));
return new JsonResponse([
'prev' => $configuration->resolveVersionAlias('prev'),
'current' => $configuration->resolveVersionAlias('current'),
'next' => $configuration->resolveVersionAlias('next'),
'latest' => $configuration->resolveVersionAlias('latest')
]);
}
You might want to set the remaining parameters as well though, especially if they differ from the defaults. For this, the configuration documentation might help in addition to the link above.

cakephp 2.6 controller test case throwing MissingActionException on duplicate testAction()-call

I hope anyone can help me out with my testing environment.
my setup
I am implementing unit tests based on phpunit 3.7.22 with the cake release 2.6.9. Running on ubuntu 12.04 LTS with PHP 5.4.43-1 and postgresql 9.1.
I immplemented controller tests mocking cakes Auth Component to have a user in the session, since my tests depent on that. My controllers return json results, since its an API for a JS-based frontend. I call my controller methods using the testAction() call of a generated controller.
<?php
App::uses('RequesttypesController', 'Svc.Controller');
class RequesttypesWithResultControllerTest extends ControllerTestCase
{
public $fixtures = array(
'app.requesttype',
'app.user',
'app.privilege',
'app.groupsprivilege',
'app.groupsuser',
'app.groupscompany',
'app.company',
);
/**
* Mock the requesttype object so that it can return results depending on the desired outcome
*
* #see CakeTestCase::setUp()
*/
public function setUp()
{
parent::setUp();
$this->controller = $this->generate('Svc.Requesttypes', array(
'models' => array(
'Requesttype'
),
'components' => array(
'Auth' => array(
'user'
),
'Session',
'RequestHandler'
)
));
$this->controller->Auth->staticExpects($this->any())
->method('user')
->will($this->returnValue(array(
'id' => 123,
'username' => 'myTestUser',
'company' => 'myTestCompany',
'usertype_id' => '456',
))
);
$authResult = $this->controller->Auth->user();
}
public function tearDown()
{
parent::tearDown();
unset($this->controller);
}
/**
* A logged in user produces a number of requesttypes
*/
public function testLoggedInUser()
{
$result = $this->testAction('/svc/requesttypes/getMyRequesttypes', array('return' => 'vars'));
$this->assertNotEmpty($this->vars, 'Did not receive webservice response');
$this->assertTrue(isset($this->vars['data']['code']), 'Received invalid webservice response');
$this->assertEqual($this->vars['data']['code'], SvcAppController::RESPONSE_CODE_SUCCESS);
}
}
?>
This test passes without errors. Now I want to test my controller-action with different setups, for example users with a different usertype, from a different company, and so on. If I now create a second test-method in my RequesttypesWithResultControllerTest-class, calling the same testAction-url, i get a MissingActionException saying:
"Action RequesttypesController::() could not be found."
It seems that the testAction calls an empty controller-action, even if the action-url is passed as a parameter. I tried reinitializing the controller by nulling it and calling $this->generate() again, but this does not help either.
Of course I can help myself out by creating an own test-controller for every test ending up in a bunch of duplicate test-code, but this somehow seems not right to me.
Am I misusing the test-environment or how can this exception be explained? Any ideas?
Thanks in advance for sharing my headache!
After some further code debugging we finally found the error. We accidently changed the require statement of the last line of the /Config/routes.php file to a require_once because of some "Class already defined Exceptions" thrown in the test-environment.
Wrong routes.php:
require_once CAKE . 'Config' . DS . 'routes.php';
For the application itself that made no difference, since the routes are only needed to be initialized once per request. But in a test-environment, the routes are reinitialized several times, which was not possible anymore with the require_once include.
This is how the line is supposed to look like, which it does by default:
Correct routes.php:
require CAKE . 'Config' . DS . 'routes.php';

How to mock a CakePHP behavior for unit testing

I've just started with unit testing in CakePHP (yay!) and ran into the following challenge. Hope somebody can help me :-)
Situation
My model uses a Behavior to send changes to an API after saving it locally. I would like to fake all calls made to the API during the test (those will be tested seperately) to save load on the API server, and more important, not actually save the changes :-)
I'm using CakePHP 2.4.1.
What I've tried
Read the docs. The manual shows how to do this for Components and Helpers but not for Behaviors.
Google. What I've found:
A Google Group post which says it simply "isn't possible". I don't take no for an answer.
An article explaining how to mock an object. Comes pretty close.
The code from the article reads:
$provider = $this->getMock('OurProvider', array('getInfo'));
$provider->expects($this->any())
->method('getInfo')
->will($this->returnValue('200'));
It might be the wrong direction, but I think that might be a good start.
What I want
Effectively: A snippet of code to demo how to mock a behavior in a CakePHP Model for unit testing purposes.
Maybe this question will result in an addition of the CakePHP manual too as an added bonus, since I feel it's missing in there.
Thanks in advance for the effort!
Update (2013-11-07)
I've found this related question, which should answer this question (partly). No need to mock up the API, instead I can create a Behavior test that the model will use.
I'm trying to figure out what that BehaviorTest should look like.
Use the class registry
As with many classes, behaviors are added to the class registry using the class name as the key, and for subsequent requests for the same object loaded from the classregistry. Therefore, the way to mock a behavior is simply to put it in the class registry before using it.
Full Example:
<?php
App::uses('AppModel', 'Model');
class Example extends AppModel {
}
class TestBehavior extends ModelBehavior {
public function foo() {
throw new \Exception('Real method called');
}
}
class BehaviorExampleTest extends CakeTestCase {
/**
* testNormalBehavior
*
* #expectedException Exception
* #expectedExceptionMessage Real method called
* #return void
*/
public function testNormalBehavior() {
$model = ClassRegistry::init('Example');
$model->Behaviors->attach('Test');
$this->assertInstanceOf('TestBehavior', $model->Behaviors->Test);
$this->assertSame('TestBehavior', get_class($model->Behaviors->Test));
$this->assertSame(['foo' => ['Test', 'foo']], $model->Behaviors->methods());
$model->foo();
}
public function testMockedBehavior() {
$mockedBehavior = $this->getMock('TestBehavior', ['foo', 'bar']);
ClassRegistry::addObject('TestBehavior', $mockedBehavior);
$model = ClassRegistry::init('Example');
$model->Behaviors->attach('Test');
$this->assertInstanceOf('TestBehavior', $model->Behaviors->Test);
$this->assertNotSame('TestBehavior', get_class($model->Behaviors->Test));
$expected = [
'foo' => ['Test', 'foo'],
'bar' => ['Test', 'bar'],
'expects' => ['Test', 'expects'], // noise, due to being a mock
'staticExpects' => ['Test', 'staticExpects'], // noise, due to being a mock
];
$this->assertSame($expected, $model->Behaviors->methods());
$model->foo(); // no exception thrown
$mockedBehavior
->expects($this->once())
->method('bar')
->will($this->returnValue('something special'));
$return = $model->bar();
$this->assertSame('something special', $return);
}
}

Unit testing a directive that defines a controller in AngularJS

I'm trying to test a directive using Karma and Jasmine that does a couple of things. First being that it uses a templateUrl and second that it defines a controller. This may not be the correct terminology, but it creates a controller in its declaration. The Angular application is set up so that each unit is contained within its own module. For example, all directives are included within module app.directive, all controllers are contained within app.controller, and all services are contained within app.service etc.
To complicate things further, the controller being defined within this directive has a single dependency and it contains a function that makes an $http request to set a value on the $scope. I know that I can mock this dependency using $httpBackend mock to simulate the $http call and return the proper object to the call of this function. I've done this numerous times on the other unit tests that I've created, and have a pretty good grasp on this concept.
The code below is written in CoffeeScript.
Here is my directive:
angular.module('app.directive')
.directive 'exampleDirective', [() ->
restrict: 'A'
templateUrl: 'partials/view.html'
scope: true
controller: ['$scope', 'Service', ($scope, Service) ->
$scope.model = {}
$scope.model.value_one = 1
# Call the dependency
Service.getValue()
.success (data) ->
$scope.model.value_two = data
.error ->
$scope.model.value_two = 0
]
]
Here is the dependency service:
angular.module("app.service")
.factory 'Service', ['$http', ($http) ->
getValue: () ->
options.method = "GET"
options.url = "example/fetch"
$http _.defaults(options)
Here is the view:
<div>
{{model.value_one}} {{model.value_two}}
</div>
I've simplified this quite a bit, as my goal is only to understand how to wire this up, I can take it from there. The reason I'm structuring it this way is because I did not initially create this. I'm working on writing tests for an existing project and I don't have the ability to configure it any other way. I've made an attempt to write the test, but cannot get it to do what i want.
I want to test to see if the values are being bound to the view, and if possible to also test to see if the controller is creating the values properly.
Here is what I've got:
'use strict'
describe "the exampleDirective Directive", ->
beforeEach module("app.directive")
beforeEach module("app/partials/view.html")
ServiceMock = {
getValue : () ->
options.method = "GET"
options.url = "example/fetch"
$http _.defaults(options)
}
#use the mock instead of the service
beforeEach module ($provide) ->
$provide.value "Service", ServiceMock
return
$httpBackend = null
scope = null
elem = null
beforeEach inject ($compile, $rootScope, $injector) ->
# get httpBackend object
$httpBackend = $injector.get("$httpBackend")
$httpBackend.whenGET("example/fetch").respond(200, "it works")
#set up the scope
scope = $rootScope
#create and compile directive
elem = angular.element('<example-directive></example-directive>')
$compile(elem)(scope)
scope.$digest()
I don't know how close I am, or if this is even correct. I want to be able to assert that the values are bound to the view correctly. I've used Vojtajina's example to set up html2js in my karma.js file to allow me to grab the views. I've done a lot of research to find the answer, but I need some help. Hopefully a programmer wiser than I can point me in the right direction. Thank you.
Create the element in karma, then use the .controller() function with the name of your directive to grab the controller. For your example, replace the last couple of lines with these:
elem = angular.element('<div example-directive></div>');
$compile(elem)($rootScope);
var controller = elem.controller('exampleDirective');
Note, that given how you defined your directive, it should be by attribute, and not as an element. I'm also not 100% sure, but I don't think you need the scope.$digest; usually I just put anything that needs to be applied into a scope.$apply(function() {}) block.

Go for Zend framework or Django for a modular web application?

I am using both Zend framework and Django, and they both have they strengths and weakness, but they are both good framworks in their own way.
I do want to create a highly modular web application, like this example:
modules:
Admin
cms
articles
sections
...
...
...
I also want all modules to be self contained with all confid and template files.
I have been looking into a way to solve this is zend the last days, but adding one omer level to the module setup doesn't feel right. I am sure this could be done, but should I? I have also included Doctrine to my zend application that could give me even more problems in my module setup!
When we are talking about Django this is easy to implement (Easy as in concept, not in implementation time or whatever) and a great way to create web apps. But one of the downsides of Django is the web hosing part. There are some web hosts offering Django support, but not that many..
So then I guess the question is what have the most value; rapid modular development versus hosting options!
Well, comments are welcome!
Thanks
You can implement sub-modules with relatively little effort in ZF. Let's say you have directory structure such as:
application/
modules/
admin/
cms/
controllers/
views/
controllers/
views/
You'd register the modules like this in your bootstrap (sub-modules use _ to separate the sub-module from the main module):
$frontController->setControllerDirectory(array(
'default' => APPLICATION_PATH . '/modules/default/controllers',
'admin' => APPLICATION_PATH . '/modules/admin/controllers',
'admin_cms' => APPLICATION_PATH . '/modules/admin/cms/controllers'
));
The issue with this is that it would actually use an underline in the URL instead of a slash, so eg: "admin_cms/conteroller/action" instead of "admin/cms/controller/action". While this "works", it's not pretty. One way to solve the issue is to provide your own route for the default route. Since the default Zend_Controller_Router_Route_Module does it almost right, you can simply extend from it and add the wanted behavior:
<?php
class App_Router_Route_Module extends Zend_Controller_Router_Route_Module
{
public function __construct()
{
$frontController = Zend_Controller_Front::getInstance();
$dispatcher = $frontController->getDispatcher();
$request = $frontController->getRequest();
parent::__construct(array(), $dispatcher, $request);
}
public function match($path)
{
// Get front controller instance
$frontController = Zend_Controller_Front::getInstance();
// Parse path parts
$parts = explode('/', $path);
// Get all registered modules
$modules = $frontController->getControllerDirectory();
// Check if we're in default module
if (count($parts) == 0 || !isset($modules[$parts[0]]))
array_unshift($parts, $frontController->getDefaultModule());
// Module name
$module = $parts[0];
// While there are more parts to parse
while (isset($parts[1])) {
// Construct new module name
$module .= '_' . $parts[1];
// If module doesn't exist, stop processing
if (!isset($modules[$module]))
break;
// Replace the parts with the new module name
array_splice($parts, 0, 2, $module);
}
// Put path back together
$path = implode('/', $parts);
// Let Zend's module router deal with the rest
return parent::match($path);
}
}
And in your bootstrap:
$router = Zend_Controller_Front::getInstance()->getRouter();
$router->addRoute('default', new App_Router_Route_Module);
What this does is traverse the path as long as it finds a module, and transparently rewrites the path so that the default Zend_Controller_Router_Route_Module can do the real work. For example the following path: "/admin/cms/article/edit" will be transformed into "/admin_cms/article/edit", which allows the standard convention of the ZF's ":module/:controller/:action" do the magic.
This allows you to have nice modular structure with self-contained modules, while still use pretty, logical URLs. One thing you want to make note of is that if you use Zend_Navigation and specify the navigation items using module/controller/action parameters, you need to tell ZF how to correctly build the URL using "/" instead of "_" in module names (by default ZF uses the :module/:controller/:action spec when it builds the URLs). You can do this by implementing your own Zend_Controller_Action_Helper_Url, like this:
<?php
class App_Router_Helper_Url extends Zend_Controller_Action_Helper_Url
{
public function url($urlOptions = array(), $name = null, $reset = false, $encode = false)
{
// Replace the _ with / in the module name
$urlOptions['module'] = str_replace('_', '/', $urlOptions['module']);
// Let the router do rest of the work
return $this->getFrontController()->getRouter()->assemble($urlOptions, $name, $reset, $encode);
}
}
And in your bootstrap:
Zend_Controller_Action_HelperBroker::addHelper(new App_Router_Helper_Url);
Now Zend_Navigation works nicely with your sub-module support as well.
I (despite of being happy ZF user) would go for Django. In ZF the "fully-modular" application is kind of holly grail. It's nearly impossible (or at least without extreme effort) to create selfcontained modules, instalable like "copy this folder into your modules directory" :) Not sure about Django, but from what I head it's simplier there...