I am upgrading tests from Codeception v2 to v4. The bootstrapping code is referenced in acceptance.suite.yml and loaded just fine.
Visible inside the _bootstrap.php file as of now there was a variable $settings, set by the surrounding Codeception code, that held information about all the live data, that was configured for the tests to run.
This variable is now gone. Printing get_defined_vars() only shows two variables set, strings that point to the current path and bootstrap file name.
How can I access the settings in bootstrapping code again?
I’ve looked at packagist, if there’d be a candidate for a split-off module, that would be of use here, but no candidate looked promising.
Edit: I’ve tried accessing the settings manually:
$settings = \Codeception\Configuration::suiteSettings('acceptance',
\Codeception\Configuration::config());
However, this only allows me access to the “static” settings, i.e., basically as written in the according YAML files. What I need are the “final” settings, i.e., the ones after the environment is evaluated.
I’ve solved this problem by switching from a bootstrap file to an extension. Example:
In codeception.yml:
extensions:
enabled:
- Bootstrapper
The class is found in lib/Bootstrapper.php via Composer’s class list feature, composer.json:
{
"autoload": {
"classmap": [
"lib/"
]
}
}
and looks something like this:
<?php
use Codeception\Events;
use Codeception\Extension;
class Bootstrapper extends Extension {
public static $events = [
Events::SUITE_BEFORE => 'beforeSuite',
];
public function beforeSuite() {
$module = 'PhpBrowser';
if ($this->hasModule('WebDriver')) {
$module = 'WebDriver';
}
/* expose info, if we're in real-browser context */
define('IS_REAL_BROWSER', $module === 'WebDriver');
/* make sure the helper functions are loaded */
require_once __DIR__.'/../tests/acceptance/_helpers.php';
}
}
Related
I am writing a simple set of PHP functions, I use only pure PHP7, no framework, nothing. These functions will later on be used in a plugin in a CMS, but that is besides the point. I want to write unit tests for my functions using Codeception (to get familiar with it, I know that Codeception essentially only runs PHPUnit here), but I don't really know how to point Codeception to my code in a reasonable way.
My structure is as follows: I have path/to/functions.php which contains the functions I want to test, something along the lines of:
<?php
namespace App;
if (!defined('CONST')) {
die('What are you doing? Get out of here!');
}
function change_string($string){
return $string . '123';
}
I have used Composer to install Codeception to the root of my project and I used Codeception bootstrap to get started and then I also used Codeception to generate the unit test file, to which I added my unit test. Now most tutorials/explanations/articles on the subject just write tests and Codeception magically knowns where to find the code to test. This makes zero sense to me, and does not work in my case. So what I did is the following:
<?php
class NamesPathsTest extends \Codeception\Test\Unit
{
/**
* #var \UnitTester
*/
protected $tester;
protected function _before()
{
defined('CONST') or define('CONST', 'XXX');
require_once('path/to/functions.php');
}
protected function _after()
{
}
// tests
public function testChangeString() {
$this->assertEquals('a123',App\change_string('a'));
}
}
This works, but I think that there must be a better way to explain to Codeception where is the code to run than using the require_once('path/to/functions.php'). How to do this? What is the smart way of pointing Codeception to my code? Can it also handle defining the constant, so that I can actually test the functions?
How does your application code knows where the functions and classes are?
The magic ingredient is called autoloading.
Since you are using Composer already, the easiest way is to configure Composer to load your classes and functions.
Autoloading only works with classes, for them you can map namespace prefix to directory,
{
"autoload": {
"psr-4": {
"Monolog\\": "src/",
"Vendor\\Namespace\\": ""
}
}
}
Files containing functions must be included, but Composer helps with that too:
{
"autoload": {
"files": ["path/to/functions.php"]
}
}
Combined result:
{
"autoload": {
"files": ["path/to/functions.php"]
"psr-4": {
"Monolog\\": "src/",
"Vendor\\Namespace\\": ""
}
}
}
Since Codeception is installed using Composer, no additional work is required to get autoloading work in tests.
To benefit from autoloading in your application code, you must require 'vendor/autoload.php'; near the entry point.
Regarding your constant question, nobody uses this way to prevent direct execution of files, it is much simpler and more secure to move the code away from public directory and leave only small index.php file that can be accessed directly in public directory.
I have a php project.
This is the composer.json
{
"require": {
"bcosca/fatfree": "3.6.4",
"sineverba/domoticz-api": "^1.0",
"sineverba/supportdate":"dev-v1.0.0-alpha"
},
"repositories": [
{
"type": "vcs",
"url": "git#github.com:sineverba/supportdate.git"
}
]
}
sineverba\domotic-api is published on packagist. sineverba\supportdate no (started tonight).
In a file Script.php under app/models folder
app
| models
Script.php
I require my 2 libraries (domoticz-api and supportdate)
I have
<?php
/**
* Main script class
*
* #since 1.0.0
*/
namespace models;
use \sineverba\domoticzapi as Api;
use \sineverba\supportdate as Supportdate;
class Script() {
//some property here
public function __construct() {
$api = new Api\Client( $this->_user,$this->_password,$this->_host );
$date = new Supportdate\SupportDate();
}
}
$api object is created correctly. $date I get the error Class 'sineverba\supportdate\SupportDate' not found
I did try also:
Remove the declaration use \sineverba\supportdate as Supportdate;
Call directly $date = new \sineverba\supportdate\SupportDate()
As said, $api (object of sineverba\domoticz-api, that is published on packagist) is instantiated correctly, so autoload mode of Fat Free Framework (the framework that I use) is working correctly.
You can also see that domoticz-api and supportdate are very similar also in composer.json.
In my webserver the folders for both libraries are correctly present under /vendor/sineverba/domoticz-api and /vendor/sineverba/supportdate
I did try also creating an index.php inside my supportdate library
<?php
require("vendor/autoload");
$date = new \sineverba\supportdate\SupportDate();
And it works.
So, we can exclude a library / composer error and a F3 framework error ('cause API is called and instantiated).
Thank you for your support
Sometimes I inadvertently add unprintable characters when typing fast. The symptoms you're experiencing fit:
one, but not another similar class works, which rules out composer dump-autoload
it works when typed in another file.
Deleting the line, then retyping slowly forces your mind to dump prior assumptions and start from scratch. This can be helpful if there are issues of unprintable characters, case differences, or any other oddities that sometimes we're blind to in the midst of development.
I have a personal PHP framework that I maintain and use for most of the work I do. Currently in the main index.php file, I have the following code which provides access to the core application class from anywhere in the project:
/**
* #return App
*/
function app() {
// Returns the main App class instance.
}
It is properly annotated with PHPDoc so that when used, any #property-read declarations or other public members of App will be available in the code hint:
Up until the new release, PhpStorm would always be aware of the contents of App and provide this code hint. Now however, the code hint doesn't work unless you are in the same namespace as App (a top level namespace) or explicitly use App or prefix all calls to app() as \app().
What's weirder is that even though the code hint isn't available outside of the top level, the quick documentation utility is perfectly aware that app() returns an App and links to the correct definition for App:
I've tried marking public as a "Sources Root" and updating my composer.json to include public in the autoload/psr4 block for "" with no change:
"autoload": {
"psr-4" : {
"": ["public/", "app/src/"]
}
}
At this stage I'm not sure whether:
This is a bug with PhpStorm that I should raise with them.
This is the expected behavior and it has just been fortunate that it worked the way I am trying to achieve for so long (use App should in fact be present for the inspector to understand what it is).
There is a trivial way either in the file structure, namespace usage or even PhpStorm settings to have it working the way it did before.
It looks like the behaviour has been corrected in a recent release (I just upgraded to 2016.1.2 Build #PS-154.1616).
Hello everyone I'm making mvc framework and want to use Twig template system installed via commposer. I used this tutorial for
MVC here is my composer.json file
{
"require": {
"twig/twig": "~1.0"
},
"autoload":{
"files":[
"config/config.php"
]
}
}
when I add require_once ROOT.'\vendor\autoload.php' to index.php or bootstrap.php I've got an error:
File 'items.php' containing class 'ItemsController' might be missing. 2. Method 'index' is missing in 'items.php'
But when I add require_once in library/View.php like this:
class View {
public $loader;
public $twig;
public $arg;
public function __construct() {
require_once (ROOT.'/vendor/autoload.php');
$loader = new Twig_Loader_Filesystem(ROOT.'/application/views');
$this->twig = new Twig_Environment($loader);
}
}
It works well where is the problem how to make it works when I add autoload outside View class? All suggestions will be helpful, thanks.
That tutorial is using the __autoload() function - this makes it incompatible with everything that comes with it's own autoloader, including Composer.
This restriction is due to PHP being unable to allow defining a function twice - so there can be only one autoload function (which was introduced in PHP 5.0). To solve the problem of wanting to add more than one autoloader, everyone starting with PHP 5.1.2 was asked NOT to define function __autoload(), but to register a function callback using spl_autoload_register().
PHP 5.1.2 was released in January 2006!
Now think about the quality of an MVC tutorial that doesn't even get autoloading right in 2015 (it got posted in 2013, which doesn't make it better).
In fact, Composer can help you here. You can use it to autoload your own classes as well, but it is easiest if you stick to PSR-4 if you use namespaces, or PSR-0 if you don't (I suggest you do, but that Tutorial does not - another low quality, I'd say).
I'm using Behat and Mink to test an application using the Laravel framework, and I want to use Artisan to set up the database before running the tests.
In order to correctly configure Artisan, I need to know the domain that will be being tested, and therefore which Laravel environment to use. This is listed in behat.yml under the various profiles being used. Eg:
default:
extensions:
Behat\MinkExtension\Extension:
base_url: http://www.example.com
...
daniel:
extensions:
Behat\MinkExtension\Extension:
base_url: http://example.dev
Is it possible to get the value of base_url from #BeforeSuite?
The main problem here seems to be that #BeforeSuite must be static, and therefore has no access to Mink, so I can't just $this->getMinkParameter('base_url').
I can access parameters with $suiteEvent->getContextParameters(), so I could duplicate the url there, but is there a cleaner solution.
The way some hooks work really freaks me out. The parameters are not passed until the context is instantiated, so there's no clean or easy way of getting them in the context before it's actually created. The best thing to do is to use #BeforeScenario event with a databaseReady flag, something like that:
protected static $databaseReady;
/**
* #BeforeScenario
*/
public function setupDatabase()
{
if (!self::$databaseReady) {
// Set it up…
self::$databaseReady = true;
}
}
But then I don't know how it's done in Laravel, but most frameworks have a config and bootstrap, including for different environments. It's a good idea to keep those things in there and use a bootstrap script (that can be run from the #BeforeSuite hook), which sets up the environment, including the database.
This is a dirty hack but if you have to do it from the #BeforeSuite hook you can get it like this:
use Behat\Testwork\ServiceContainer\Configuration\ConfigurationLoader;
$config = new ConfigurationLoader('BEHAT_PARAMS', getcwd() . '/behat.yml'))->loadConfiguration();
$baseUrl = $config[0]['extensions']['Behat\\MinkExtension']['base_url'];