I'm testing Behat/Mink for the first time with a simple example.
When I launch behat I have this error :
PHP Fatal error: Class 'Behat\Behat\Context\Step\Given' not found in /var/www/behat-test/features/bootstrap/FeatureContext.php on line 31
features/bootstrap/FeatureContext.php :
<?php
require_once './vendor/autoload.php';
use Behat\Behat\Context\Context;
use Behat\Behat\Context\SnippetAcceptingContext;
use Behat\MinkExtension\Context\MinkContext;
use Behat\Behat\Context\Step;
class FeatureContext extends MinkContext implements Context, SnippetAcceptingContext
{
// ......
/**
* #Given I am logged in as :username
*/
public function iAmLoggedInAs($username)
{
return array(
new Step\Given('I go to "login.php"'), // line 31
new Step\When('I fill in "My name" with '.$username),
new Step\When('I press "Login"')
);
}
}
In Behat2, Given/When/Then classes were used for step chaining. Since this technique brought more problems (with maintenance) then benefits, they're no longer supported in Behat3 (which you apparently use). It's also not recommended to follow this practice.
See https://github.com/Behat/Behat/issues/546.
Related
I am trying to use overload option of Mockery library on Laravel 5.
My current environment:
Laravel 5
Mockery 1.0
PHPUnit 7.5
I wrote this test case:
namespace Tests\Unit;
use Mockery;
use Tests\TestCase;
/**
* #runTestsInSeparateProcesses
* #preserveGlobalState disabled
*/
class RenewSignatureTest extends TestCase
{
public function testHandle()
{
$mock = Mockery::mock('overload:App\FooClass');
$mock->shouldReceive('callBar')
->times(2);
}
}
According to documentation, this test should fail, but does not matter what I do, the test never fails! It always result in:
Time: 304 ms, Memory: 19.53 MB
OK (1 test, 1 assertion)
If I remove the overload: option, the test fails. So I assume that I'm not using the library's methods as expected.
The new test:
namespace Tests\Unit;
use Mockery;
use Tests\TestCase;
/**
* #runTestsInSeparateProcesses
* #preserveGlobalState disabled
*/
class RenewSignatureTest extends TestCase
{
public function testHandle()
{
$mock = Mockery::mock('App\FooClass');
$mock->shouldReceive('callBar')
->times(2);
}
}
The result:
Mockery\Exception\InvalidCountException: Method callBar(<Any Arguments>) from Mockery_0__App_FooClass should be called exactly 2 times but called 0 times.
Am I doing anything wrong? Does anyone know how to use this option properly?
Reading the page, I think this is the error you are looking for:
When Mockery overloads a class, because of how PHP works with files,
that overloaded class file must not be included otherwise Mockery will
throw a “class already exists” exception. This is where autoloading
kicks in and makes our job a lot easier.
The error that you're looking for will be caused if you remove these 2 lines from your test, which are added to the code in the second code sample, and in the third sample on the manual page:
* #runTestsInSeparateProcesses
* #preserveGlobalState disabled
This means your code wont take advantage of the psr4 autoloader or any autoloader that is in place, and will create a new autoloader for you, at the expense of speed, since it wont be using your dumped classmap and has to build it up from scratch.
If you take the two lines above out, you will get the expected error, as it will try to overload your class with a class of the same name. That class will already be autoloaded, so you get a fatal error.
So if you want to block calls to callBar, and return void, that is what your code will be doing, which is correct.
Removing overload will mean your mock is no longer effective, as you will have to pass it through a constructor to get it to work.
Update:
With your update, I can conclude that your code must be running the mocked callBar method twice (not the actual callBar method), with your mock of fooBar class using overload. When the mocked method gets called, nothing inside the real callBar method actually happens, it just registers that it was called. If you're expecting it once for example, write shouldReceive(1) and then fix the code that your test runs.
When you remove the overload, the global injection doesnt take place, so your mock never gets injected. However, your mock callBar method on the mock class is still expecting to be ran twice, so you get the error. You will need to remove the 2 mock code lines completely from your test.
Keep the # statements in, as it will help prevent the psr4 error outlined above.
This is not the way to test, you should never use overload: option... Laravel helps you with a lot of things, don't create the wheel again or try to...
Why would you need to use #runTestsInSeparateProcesses and #preserveGlobalState disabled ?
Let's say your test is like this:
namespace Tests\Unit;
use App\FooClass;
use App\Service\ServiceForTesting;
use Mockery;
use Tests\TestCase;
class RenewSignatureTest extends TestCase
{
public function testHandle()
{
$mock = Mockery::mock(FooClass::class);
$mock->shouldReceive('callBar')
->times(2);
$myClass = app(ServiceForTesting::class);
$myClass->run($mock);
$myClass->run($mock);
}
}
You should have a code like this:
namespace App;
class FooClass
{
public function callBar()
{
return 'Works !!!';
}
}
Now, let's say you have a standalone class (no controller or similar, you are in a unit test), you should have something like this:
namespace App\Service;
use App\FooClass;
class ServiceForTesting
{
public function run(FooClass $class)
{
return $class->callBar();
}
}
My question is in three parts:
Does putting in a use statement trigger the autoloader immediately, or does it wait until the class is used? (lazy-loading)
If autoloading isn't done in a lazy-load fashion, could that negatively affect performance?
Which pattern is best to follow, and why? PhpStorm shows "Unnecessary fully qualified name..." as a code issue when the use statement isn't employed.
Here's an example class definition for a Laravel controller with a use statement:
namespace App\Http\Controllers;
use Carbon\Carbon;
class FooController extends Controller
{
/**
* This action uses the Carbon class
*/
public function bar1()
{
return view('foo.bar1', ['now' => new Carbon()]);
}
/**
* This action does not use the Carbon class
*/
public function bar2()
{
return view('foo.bar2');
}
}
The same class without the use statement:
namespace App\Http\Controllers;
class FooController extends Controller
{
/**
* This action uses the Carbon class
*/
public function bar1()
{
return view('foo.bar1', ['now' => new \Carbon\Carbon()]);
}
/**
* This action does not use the Carbon class
*/
public function bar2()
{
return view('foo.bar2');
}
}
1) The class is autoloaded when you perform a new Class() statement.
2) see 1)
3) Which pattern is best to follow and why?:
I'd recommend to use use because you might get into a situation where you have really long namespaces and your code will become unreadable.
From the php docs:
This example attempts to load the classes MyClass1 and MyClass2 from
the files MyClass1.php and MyClass2.php respectively.
<?php
spl_autoload_register(function ($class_name) {
include $class_name . '.php';
});
$obj = new MyClass1();
$obj2 = new MyClass2();
?>
Namespaces are only an additional feature to organize classes.
EDIT: As #IMSoP pointed out in the comments, new is not the only time the autoloader is triggered. Accessing a class constant, static method, or static property will also trigger it, as will running class_exists.
The use statement can be thought of like a C pre-processing macro, if you're familiar with those: it rewrites the current file at compile time to let you write a short name for a long class, function, or constant name. It doesn't trigger autoloading, as it doesn't care if a class exists or not.
For instance, if you write use Foo\Bar\Baz as X, then everywhere that X is mentioned as a class name, the PHP compiler rewrites that to mention Foo\Bar\Baz instead. Only when code mentioning the class (e.g. new X, X::FOO, X::doSomething()) is actually run does it see if there really is a class Foo\Bar\Baz, and trigger the autoloader as necessary.
The common form use Foo\Bar\Baz is just shorthand for use Foo\Bar\Baz as Baz, assigning the alias Baz to the class name Foo\Bar\Baz.
As the manual points out the alias is only processed at compile time, so dynamic lookups will not use it. In the example above, class_exists('X') will return false, but you can use class_exists(X::class) to expand out the alias - the compiler will automatically substitute the full class name as a string, so at run-time, the expression will be class_exists('\Foo\Bar\Baz').
Whether use statements make your code better is therefore entirely a matter of style: the intent is that your code will be more readable without the long fully-qualified class names, but it will make no difference to how the code actually runs.
I'm trying to manipulate the login target path in Symfony security, since my app makes some AJAX calls. I followed this documentation article, but nothing happens (so I assumed security.exception_listener.class didn't "hook" properly). When I googled I found this issue from 2015 on github which claims the solution in the provided documentation would be invalid in Symfony >= 3.
Now I'm just wondering - does anyone know if the linked documentation is actually out of date (which seems to be the case as it does not work for me), and how do you accomplish the same thing in Symfony 3?
The documentation for Symfony 2.7 has been updated to use a compiler pass: https://symfony.com/doc/2.7/security/target_path.html
I imagine that this change will flow through to the later versions of the documentation soon.
// src/AppBundle/Security/Firewall/ExceptionListener.php
namespace AppBundle\Security\Firewall;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Firewall\ExceptionListener as BaseExceptionListener;
class ExceptionListener extends BaseExceptionListener
{
protected function setTargetPath(Request $request)
{
// Do not save target path for XHR requests
// You can add any more logic here you want
// Note that non-GET requests are already ignored
if ($request->isXmlHttpRequest()) {
return;
}
parent::setTargetPath($request);
}
}
// src/AppBundle/DependencyInjection/Compiler/ExceptionListenerPass.php
namespace AppBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use AppBundle\Security\Firewall\ExceptionListener;
class ExceptionListenerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
// Use the name of your firewall for the suffix e.g. 'secured_area'
$definition = $container->getDefinition('security.exception_listener.secured_area');
$definition->setClass(ExceptionListener::class);
}
}
// src/AppBundle/AppBundle.php
namespace AppBundle;
use AppBundle\DependencyInjection\Compiler\ExceptionListenerPass;
class AppBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
$container->addCompilerPass(new ExceptionListenerPass());
}
}
The basic Behat use case recommends using FeatureContext class. Also, you can specify any other PHP classes in the features/bootstrap directory and they are loaded, but in the alphabetical order, no matter what the dependencies are.
Given there is a trait and a FeatureContext class:
features/bootstrap/FeatureContext.php
features/bootstrap/MyLovelyTrait.php
What is the best way to load it properly? Obviously, MyLovelyTrait is used within the FeatureContext:
class FeatureContext extends BehatContext {
use MyLovelyTrait;
}
And that fails because M > F, in the alphabet.
I will be happy to use composer autoloading, but I don't want to require_once the autoload.php file in the top of BehatContext.php file. Is there a way to specify this in behat.yml configuration? Also, any other best practice answer regarding class-loading of Behat context files will be appreciated.
I'm not 100% sure this is answering your question but I am under the impression that you are trying to use multiple context files? If so you don't need the use statement instead within the FeatureContext.php construct method we use the line:
$this -> useContext('Subcontext', new Subcontext($parameters));
In this case the other context you want to use is called "Subcontext".
A good reason not to useContext('Subcontext') can be found in the Changelog of the upcoming version 3 of Behat:
3.0.0beta1 / 2013-08-13
...
* Subcontexts removed in favor of context pools
I hacked around it by working with the grain of behat -- all my traits start with "A". Examples:
// FeatureContext.php is at features/bootstrap/FeatureContext.php
<?php
use Behat\Behat\Context\ClosuredContextInterface,
Behat\Behat\Context\TranslatedContextInterface,
Behat\Behat\Context\BehatContext,
Behat\Behat\Exception\PendingException;
use Behat\Gherkin\Node\PyStringNode,
Behat\Gherkin\Node\TableNode;
class FeatureContext extends BehatContext
{
use AWebDriverContextTrait;
}
and
// AWebDriverContextTrait is at features/bootstrap/AWebDriverContextTrait.php
<?php
trait AWebDriverContextTrait {
/**
* #Given /^I am on "([^"]+)"/
*/
public function iAmOnSite($url)
{
$this->driver = new \Behat\Mink\Driver\Selenium2Driver(
'firefox',
''
);
$this->session = new \Behat\Mink\Session($this->driver);
$this->session->start();
$this->session->visit($url);
}
private $driver;
private $session;
}
I am trying to use Doctrine Common to create my own annotation. http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/annotations.html does not match https://github.com/doctrine/common because Call to undefined method Doctrine\\Common\\Annotations\\AnnotationReader::setDefaultAnnotationNamespace and PHP Fatal error: Call to undefined method Doctrine\\Common\\Annotations\\AnnotationRegistry::registerAnnotationNamespace. I checked the source, they are not there. According to git log they were removed a year ago.
I have a PSR-0 autoloader going (from Symfony). So I have a file where the PSR-0 loader expects:
namespace My\Test;
/**
* #Annotation
*/
class Foo {
}
Another class
namespace My\Annotated
/**
* #My\Test\Foo
*/
class Test {
}
Reader:
namespace My\Reader
use ReflectionClass;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;
use My\Test\Foo;
$reader = new AnnotationReader();
$reflectionClass = new ReflectionClass('My\\Annotated\\Test');
$classAnnotations = $reader->getClassAnnotations($reflectionClass);
var_dump($classAnnotations);
Gets: "[Semantical Error] The annotation "#My\\Test\\Foo" in class My\\Annotated\\test was never imported. Did you maybe forget to add a "use" statement for this annotation?" I ilikely did but for my life I can't figure out where to add it. And I really would like to just use #Foo if possible.
I stumbled accross this issue with error messages like:
Fatal error: Uncaught exception
Doctrine\Common\Annotations\AnnotationException' with message '[Semantical Error]
The annotation "#Symfony\Component\Validator\Constraints\NotNull" in property My\Namespace\Model\Foo\Bar::$name does not exist, or could not be auto-loaded.'
in var/www/virtual/hive/current/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php on line 54
while I was trying to get the symfony2 Validator component to work as a standalone in a legacy non-symfony2 project.
I was also under the impression that the annotations should already be autoloaded, whereas one indeed has to specifically realize that one has to use doctrine's autoloader in order to get the annotations working.
I have looked at how symfony2 loads the registry, and they do it like this in the app/autoload.php
<?php
use Doctrine\Common\Annotations\AnnotationRegistry;
use Composer\Autoload\ClassLoader;
/**
* #var ClassLoader $loader
*/
$loader = require __DIR__.'/../vendor/autoload.php';
AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
return $loader;
My project uses a bootstrap.inc.php file that always will be loaded and it had:
require_once PROJECT_PATH . '/vendor/autoload.php';
I just refactored it to look similar:
use Doctrine\Common\Annotations\AnnotationRegistry;
...
$loader = require_once PROJECT_PATH . '/vendor/autoload.php';
AnnotationRegistry::registerLoader([$loader, 'loadClass']);
And then the Annotation were found.
Autoloading is apparently handled by AnnotationRegistry::registerAutoloadNamespace which is a PSR-0 autoloader. Documentation/source.
I found that you can do a use My\Test\Foo in the annotated file to use #Foo as an annotation. And the reason for that is possible is because Doctrine re-parses the file solely for use statements.