PHPUnit & Selenium - How to get driver being used by a test? - php

My general test setup looks something like:
class MySeleniumTest extends PHPUnit_Extensions_SeleniumTestCase{
public static $browsers = array(
array(
'name' => 'Mozilla - Firefox',
'browser' => '*firefox',
'host' => 'localhost',
'port' => 4444,
'timeout' => 30000,
),
array(
'name' => 'Google - Chrome',
'browser' => '*googlechrome',
'host' => 'localhost',
'port' => 4444,
'timeout' => 30000,
)
);
//etc
}
And from here an individual test file looks something like:
class MyTest extends MySeleniumTest{
public function setUp(){
parent::setUp();
$this->setUser(1);
}
public function testPageTitle(){
//Login and open the test page.
$this->login(8);
$this->open('/test/page');
//Check the title.
$this->assertTitle('Test Page');
}
}
From here, when I run MyTest.php with PHPUnit, PHPUnit will automatically run each test case in MyTest.php. Furthermore, it runs each test on each of the specified browsers individually. What I want to be able to do is get information on the driver running a specific test case from within that test case. So something like:
public function testPageTitle(){
//Login and open the test page.
$this->login(8);
$this->open('/test/page');
//Check the title.
$this->assertTitle('Test Page');
$driver = $this->getDriver();
print($driver['browser']); //or something.
}
This however, does not work. And $this->getDrivers() just adds more drivers to the tests, and is only suppose to be used by the setup. Any ideas? Thanks!

Even though $this->drivers is an array there is always only one element in it. You can check that here. So
$this->drivers[0] contains informations about currently running browser and you can use $this->drivers[0]->getBrowser() to output browser name.
Example:
require_once 'MySeleniumTest.php';
class MyTest extends MySeleniumTest{
public function setUp(){
parent::setUp();
$this->setBrowserUrl('http://www.google.com/');
}
public function testPageTitle(){
$this->open('http://google.com');
echo "{$this->drivers[0]->getBrowser()}\n";
}
}
Outputs:
PHPUnit 3.7.18 by Sebastian Bergmann.
.*firefox
.*googlechrome
Time: 7 seconds, Memory: 3.50Mb
OK (2 tests, 0 assertions)

Related

relationship is not working - Laravel / phpunit / Sqlite

I am coding a test, but I get some unexpected results in basic relationships. I use in memory sqlite db.
Those test were working well with MySQL db. I guess it has something to do with this migration.
$tournament0 = factory(Tournament::class)
->create(['user_id' => $this->simpleUser->id]);
$this->simpleUser is created on setUp() method
Now, when $this->simpleUser->tournaments should return $tournament0, but it is empty.
My relationship is working, it is not broken. Also, when testing manually in the browser, it works.
It just fail when using phpunit...
Here is the setUp() Method:
public function setUp()
{
parent::setUp();
$this->simpleUser = factory(User::class)->create(['role_id' => Config::get('constants.ROLE_USER')]);
}
Full method that is failing:
/** #test */
public function dashboard_check_initial_state()
{
Artisan::call('db:seed', ['--class' => 'CountriesSeeder', '--database' => 'sqlite']);
Artisan::call('db:seed', ['--class' => 'TournamentLevelSeeder', '--database' => 'sqlite']);
Artisan::call('db:seed', ['--class' => 'CategorySeeder', '--database' => 'sqlite']);
// Given
$this->logWithUser($this->simpleUser);
// Nothing has been created, default dash
$this->visit('/')
->see(trans('core.create_new_tournament'))
->dontSee(trans('core.congigure_categories'));
// Create 1 tournament
$tournament0 = factory(Tournament::class)->create(['user_id' => $this->simpleUser->id]);
$this->visit('/')
->seeInElement("p.text-muted", trans('core.no_tournament_created_yet'));
// Now configure 2/2 categories
$championship1 = factory(Championship::class)->create(['tournament_id' => $tournament0->id,'category_id'=>1]);
$championship2 = factory(Championship::class)->create(['tournament_id' => $tournament0->id,'category_id'=>2]);
factory(ChampionshipSettings::class)->create(['championship_id' => $championship1->id]);
factory(ChampionshipSettings::class)->create(['championship_id' => $championship2->id]);
$this->visit('/')
->seeInElement("span.text-muted", trans('core.congigure_categories'));
}
Any idea what can it be?

How to configure CakePHP's $this->Auth->login() to use custom password hasher

CakePHP v.2.4...
I'm following this documentation trying to set up the Auth component to use my custom password hashing class:
App::uses('PHPassPasswordHasher', 'Controller/Component/Auth');
class AppController extends Controller {
// auth needed stuff
public $components = array(
'Session',
'Cookie',
'Auth' => array(
'authenticate' => array(
'Form' => array(
'fields' => array('username'=>'email', 'password'=>'password'),
'passwordHasher' => 'PHPass'
)
),
Inside my UsersController::login() I debug the return from $this->Auth->login(); and it always returns false, even when I submit the correct email / password.
(NOTE: It looks strange to me that the login() takes no parameters, but the docs seem to imply that it looks into the the request data automatically. And this would make sense if my configurations aren't correctly causing it to check the User.email field instead username.)
The post data from the submitted login form looks like this:
array(
'User' => array(
'password' => '*****',
'email' => 'whatever#example.com'
)
)
What am I missing?
Update2
I'm starting to suspect that the default hashing algorithm is getting used instead of my custom class. I tried to match the examples in the docs but they're quite vague on how to do this.
Here's the contents of app/Controller/Component/Auth/PHPassPasswordHasher.php
<?php
App::import('Vendor', 'PHPass/class-phpass'); //<--this exists and defines PasswordHash class
class PHPassPasswordHasher extends AbstractPasswordHasher {
public function hash($password) {
$hasher = new new PasswordHash( 8, true );
return $hasher->HashPassword($password);
}
public function check($password, $hashedPassword) {
debug('PHPassHasher'); die('Using custom hasher'); //<--THIS NEVER HAPPENS!
$hasher = new new PasswordHash( 8, true );
return $hasher->CheckPassword($password, $hashedPassword);
}
}
AHA! The debug() never appears... so I'm pretty sure the problem is with my custom hasher configuration(s).
Update3
Additional clue: I experimented by setting various default hashing algorithms (Ex: "Simple", "Blowfish") and creating users. The hashes which show up in the DB are all the same which tells me that my config settings are getting ignored completely.
Update4
I debugged $this->settings inside the constructor of /lib/Cake/Controller/Component/Auth/BaseAuthenticate.php and my custom hasher settings are in there:
array(
'fields' => array(
'password' => 'password',
'username' => 'email'
),
'userModel' => 'User',
'scope' => array(),
'recursive' => (int) 0,
'contain' => null,
'passwordHasher' => 'PHPass'
)
You need to rename your password hasher class to have the suffix "PasswordHasher", and only provide the non-suffixed name in the 'className' argument.
eg:
<?php
App::import('Vendor', 'PHPass/class-phpass'); //<--this exists and defines PasswordHash class
class PHPassHasherPasswordHasher extends AbstractPasswordHasher {
// functions
}
The example from the docs sets the classname to 'Simple', which then loads 'SimplePasswordHasher'.
You might find that having a name of PHPassHasherPasswordHasher is a bit silly, it's up to you what you want to call it. Perhaps PHPassPasswordHasher might be a bit more appropriate (and then use the classname argument 'PHPass').
EDIT: It seems as if Cake has issues when multiple capital letters are used one after the other (eg. PHPass), so the right way to do it is to change the password hasher class to the following:
<?php
App::import('Vendor', 'PHPass/class-phpass'); //<--this exists and defines PasswordHash class
class PhpassPasswordHasher extends AbstractPasswordHasher {
// functions
}
... and make sure the filename matches the classname: PhpassPasswordHasher.php.
Thanks to SDP for the discussion, I learnt something today!
According to the docs:
To configure different fields for user in $components array:
// Pass settings in $components array
public $components = array(
'Auth' => array(
'authenticate' => array(
'Form' => array(
'fields' => array(
'username' => 'email',
'password' => 'password'
)
)
)
)
);
Source
I finally got this working. We were on the right track by renaming the file/class to comply with Cake conventions. I had to go one step further and change the capitalization as well:
PHPassPasswordHasher.php --> PhpassPasswordHasher.php
class PHPassPasswordHasher... --> class PhpassPasswordHasher...
Phew!
ps: Many many thanks to #Ben Hitchcock for support on this.

Where should be stored the environment dependent configs in a Zend Framework 2 application?

A ZF2 application contains/nneds a lot of different config files: /config/application.config.php, /config/autoload/global.php, /config/autoload/local.php, /module/***/config/module.config.php.
Now I've written a module, that covers the caching functionality for the application, and need different values for the livetime of its items in my local/dev and the live environment. I also would like to be able to switch the cache type dependent on the environment.
Where should such stuff be sored? In /config/autoload/global.php and /config/autoload/local.php? If yes: should it first retrieved from these files in the Module class (e.g. in the onBootstrap() method) or used directly, where it's needed?
(It also would be great, if someone could show a primitive example for saving and getting such config data.)
The solution, I'm currently using is:
/config/autoload/global.php and/or /config/autoload/local.php
return array(
// DB credentials
'db' => array(
'username' => ...,
'password' => ...,
'dbname' => ...,
'host' => ...,
),
'cache_ttl' => 'global/local value for cache live time',
);
Cache Module class
class Module {
private $moduleConfig;
public function onBootstrap(MvcEvent $mvcEvent) {
$application = $mvcEvent->getParam('application');
$this->moduleConfig = $application->getConfig();
}
...
public function getServiceConfig() {
try {
return array (
'factories' => array(
...
'Zend\Cache\Adapter\MemcachedOptions' => function ($serviceManager) {
return new MemcachedOptions(array(
'ttl' => $this->moduleConfig['cache_ttl'],
...
));
},
...
)
);
}
...
}
}
It works fine, but I'm pretty sure, that it's not the best practice / recomended way.
Your basic approach is the correct one.
For cache configuration stuff, keep your production values in the global file. That should live in your VCS. (EDIT: however, you should probably omit security sensitive configuration such as database passwords. Add that stuff to production via a local.php to keep it out of version control).
In your local environment, use the local file to override anything that needs to be overridden. IIRC the ZendSkeletonApplication has a .gitignore file that will ignore any local configs built in -- so your local configuration never makes it into git.
However, you don't need to mess around with loading the config on bootstrap like you are. You can just grab the config from the serviceManager inside your factory method:
public function getServiceConfig() {
try {
return array (
'factories' => array(
...
'Zend\Cache\Adapter\MemcachedOptions' => function ($serviceManager) {
return new MemcachedOptions(array(
// you can just grab your config from the service-manager
'ttl' => $serviceManager->get('Config')['cache_ttl'],
...
));
},
...
)
);
}
...
}
Also - I wouldn't stick 'cache_ttl' as a top-level config key. Instead, try:
global.php
return array(
'cache' => array(
'ttl' => ...,
'servers' => ...,
...
)
);
That simplifies your factory to just something like:
'Zend\Cache\Adapter\MemcachedOptions' => function ($serviceManager) {
return new MemcachedOptions( $serviceManager->get('cache') );
},
and you can override whatever you want in your local.php config. If all you want to do is change the ttl (leaving all the other global configs):
local.php
return array(
'cache' => array('ttl'=>...)
);

pre_controller clashes with post_controller_constructor

I want to check if the user has permission to access to the URL (controller/method) combination. It should check before any method called in called controller and method belongs to it.
As far as I understand, the hook should be pre_controller for the logic above but when I use it, I think it clashes with post_controller_constructor shown below. If I use post_controller instead then it works but this time logic is compromised.
How can I solve this problem?
Thanks
CONFIG/HOOKS
//Used to authenticate user session to decide whether to authenticate site or not
$hook['post_controller_constructor'] =
array(
'class' => 'site_authentication',
'function' => 'authenticate',
'filename' => 'site_authentication.php',
'filepath' => 'hooks',
'params' => null
);
//Used to authenticate permitted controllers
$hook['pre_controller'] =
array(
'class' => 'permitted_controllers',
'function' => 'authenticate',
'filename' => 'permitted_controllers.php',
'filepath' => 'hooks',
'params' => null
);
APPLICATION/HOOKS
//This works fine
class site_authentication
{
private $CI;
public function __construct()
{
$this->CI =& get_instance();
}
public function authenticate()
{
if (! $this->CI->session->userdata('site'))
{
redirect('to error page');
}
$user_session = $this->CI->session->userdata('site');
//Some more stuff here
}
}
//This doesn't work with pre_controller
class permitted_controllers
{
private $CI;
public function __construct()
{
$this->CI =& get_instance();
}
public function authenticate()
{
$user_session = $this->CI->session->userdata('site');
//Url is set here, ignore syntax error below
$url = $this->CI->uri->segment(1) . 2 . 3;
if (! in_array($url, $user_session['controllers']))
{
redirect('to error page');
}
}
}
If I combine them two, they work fine under post_controller_constructor but they won't work separately?
$hook['post_controller_constructor'] [] =
array(
'class' => 'site_authentication',
'function' => 'authenticate',
'filename' => 'site_authentication.php',
'filepath' => 'hooks',
'params' => null
);
$hook['post_controller_constructor'] [] =
array(
'class' => 'permitted_controllers',
'function' => 'authenticate',
'filename' => 'permitted_controllers.php',
'filepath' => 'hooks',
'params' => null
);
pre_controller hook is run before the super object has been constructed, so it is not a viable option for hooking into CI's normal syntax (such as $this->db->query()).
I'd suggest creating a base controller (aka MY_Controller or some other name) and adding the permission check to its constructor. Then, each controller that should run the permissions check will then extend MY_Controller instead of CI_Controller. Here's Phil Sturgeon's classic article about base controllers.
Hooks are called on every page load. If you don't need to check permissions somewhere, you need to either add that logic to your hook, or add logic somewhere else to try and disable it. Not very extendable. Using a base controller, adding the permission check is as simple as extending a different class.
The pre_controller hook executes before the super object has been fully constructed read more here

2 Unit test controllers to verify that action created new record

How can I assert that a new record was created in my Member's test database when I unit test the Members controller's register action?
I'm trying to use getLastInsertID but I don't know how to load the Member model to make the call.
I already have tests for my Member model which cover register and activate, but there is an error when I go through the activation process. I would like to use a test in the controller to verify that a new member can be registered and then activated using two tests.
Update
I added App::uses('Member', 'Model'); to my Controller test, and also
public function setUp() {
parent::setUp();
$this->Member = ClassRegistry::init('Member');
}
in function testRegister I have:
$data = array(
'Member' => array(
'first_name' => 'Foo',
'last_name' => 'Bar',
'email' => 'foobar#example.org',
'password' => 'password',
'password_confirmation' => 'password',
'security_question' => 0, // Your father's middle name?
'security_answer' => 'Randolf the great',
),
);
$result = $this->testAction(
'/Members/register',
array('data' => $data, 'method' => 'post')
);
debug($result);
$this->assertTrue($this->Member->validationErrors);
$this->assertTrue($this->Member->getLastInsertId());
The assertions show "Failed to assert that array() is true" and "failed to assert that null is true".
I was able to find enough information in $this->vars to find the record with my Model. Everything tests out now.

Categories