I am successfully using Behat 3.0 with the tests defined in Feature files, using Gherkin language. However, in some cases, it would be useful to define the steps programatically - Gherkin is readable, but difficult to define multiple variants.
Is there a way to programatically define the test steps (in PHP classes), so these can be picked up by Behat? I have found ArrayLoader class, which seems to be able to do that. However, I wasn't able to make it working with Behat. It seems Behat is using Gherkin FileLoader by default and I haven't found a way to rewrite this behavior (or rather extend) in the config file.
How can I combine test input from Gherkin files with custom definitions specified in PHP files?
Is there a way to programatically define the test steps (in PHP
classes),...
If I don't misunderstand what you want, you can do like this:
use Behat\Behat\Definition\Call\Then;
use Behat\Behat\Definition\Call\When;
use Behat\MinkExtension\Context\MinkContext;
class FeatureContext extends MinkContext
{
public function iWaitSeconds($second)
{
new Then(.....);
new When(.....);
new Given(.....);
}
}
You need to do a bit of research for more examples.
e.g.: new When("The content is:", new PyStringNode($string));
Related
I need to refactor a PHP project where the vendor has undergone a re-brand. The project currently uses the namespace OldCompany, and I need to change this to NewCompany, however I've realized I need to keep the old namespace for backwards compatibility, in the cases where existing users are using try {} catch (/OldCompany/Exception $e) {}... If I simply change the namespace to NewCompany, I will break their integration if they upgrade SDK versions straight up. After reading the PHP Namespace docs, I tried the method outlined in Example #3, and modified all of my files like this:
<?
namespace NewCompany{ /* no namespace-specific code needed */ };
namespace OldCompany{ /* no namespace-specific code needed */ };
namespace {
/* global namespace code. code that applies to both namespaces? */
require_once('file1.php');
require_once('file2.php');
/* classes and functions within the global namespace */
}
The above throws a PHP Fatal Exception and can't find the NewCompany namespace.
I definitely do not want to duplicate code as per Example #2 of the docs, since there isn't namespace-specific code.
What is the best way to preserve the existing namespace of OldCompany for existing users while refactoring a re-brand to `NewCompany' for new users? Should I be looking for a different solution to this problem?
Thanks in advance :)
namespace NewCompany{ /* no namespace-specific code needed */ };
namespace OldCompany{ /* no namespace-specific code needed */ };
This is setting namespaces. But surely your issue is old vendor namespace has changed to a new one? This means you need to import (use) the new namespace instead of the old one?
Maybe I've misunderstood you, but are you confused about the difference between setting and importing namespaces? If the vendor has changed to a new namespace then you need to import the new one, rather than the old one. But this has nothing to do with setting namespaces.
I definitely do not want to duplicate code as per Example #2 of the
docs, since there isn't namespace-specific code.
If there's no namespace specific code then what problem are you trying to resolve?
I need to keep the old namespace for backwards
compatibility, in the cases where existing users are using
try {} catch (/OldCompany/Exception $e) {}.
Surely whatever namespace they have in their end won't affect your side of things? So you could update all your code's namespaces and not worry about what they use? They just call your endpoint or whatever as normal?
Perhaps be more specific about that if it's a real issue somehow.
It sounds to me like you just need to update your import statements for the vendor's new namespace.
Something else to consider is refactor how you manage vendors.
I presume you are not using a pre-made framework, such as Symfony (they have predetermined ways of managing vendors and things).
The fact you are considering refactoring throughout your code rather than a single config file (or whatever) makes me think your code has a design flaw. As it seems you are changing code (namespace) within your class files based on a 3rd party company (vendor) changing their name. And where possible your code should be entirely abstract from such changes to this degree.
I suggest considering abstracting things out into centralised places whenever it makes sense. This allows the one centralised thing to be altered and changes just automatically ripple down to all your code without any need for a huge refactor.
You could make your own generic names for your vendors so whatever they call themselves doesn't matter in your code.
E.g. vendor FunkyJoesEmailer in your app will just be Emailer. Then whichever emailer library you decide to use now and in the future will be in the same Emailer DIR and namespaces won't change, always be Whatever\Emailer.
Then in some file high up the load chain you'd have some wrapper class (or service or container like thing) which would load FunkyJoesEmailer in whatever namespace that is in via your generic name, such as $this->Emailer. So in your code you'd call on $this->Emailer which would return an instance of whatever emailer (vendor) you are using.
If you ever needed to do a namespace change or even entirely swap out the Emailer vendor you use, the change is in one place and would ripple down in your code because it's still $this->Emailer.
While this approach doesn't resolve your having to change everything now, it does mean you only ever have to change it this once. Then in the future can just replace vendors or let their renaming happen within their code and your path (namespace) to it remains the same.
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).
I'm testing with PHPSpec, and I've got custom matchers in my spec files that I don't want to have to repeat in every file in which they're used.
I want to be able to extend PHPSpec's behaviour and/or the behaviour of all my tests to use a global set of custom matchers.
The PHPSpec documentation shows how to create custom matchers (inline matchers):
http://phpspec.readthedocs.org/en/latest/cookbook/matchers.html#inline-matcher
I have this inline matcher (as an example) in my ThingImTestingSpec class:
public function getMatchers()
{
return [
'haveSecondLevelKey' => function ($subject, $key) {
foreach ($subject as $first_level_key => $second_level_array)
{
if (is_array($second_level_array))
{
return array_key_exists($key, $second_level_array);
}
}
return FALSE;
}
];
}
This inline matcher example detects whether an array has an array as one of its values, and returns true or false.
As far as I can tell, PHPSpec calls getMatchers() when it constructs ThingImTestingSpec, if a getMatchers() method is present (otherwise PHPSpec ends up calling the empty getMatchers() method on ObjectBehavior.
What I've tried:
Create a CustomMatchers class and namespace it:
namespace SpecUtilities;
class CustomMatchers
{
public static function getMatchers()
{ ... }
}
and add this to my spec file:
use SpecUtilities\CustomMatchers
and in the class itself:
function it_pulls_in_custom_matchers()
{
CustomMatchers::getMatchers();
}
but the return from getMatchers() when the tests are run doesn't get used for anything. PHPSpec only seems to be using the return from getMatchers() when it constructs the test (which makes sense - getMatchers() only returns an array of functions; it doesn't attach those functions to anything else, so PHPSpec isn't using them). I get the error
no haveSecondLevelKey([array:1]) matcher found for [array:14].
i.e. PHPSpec isn't loading the custom matchers.
All spec classes extend ObjectBehavior. I could add my getMatchers() function to PHPSpec's ObjectBehavior class, but I don't want to modify files under /vendor (PHPSpec is being pulled in using Composer). I could copy the vendor file and modify my own CustomObjectBehavior class, and make my spec classes extend that instead, but that will break the usability of PHPSpec's generator methods like phpspec describe SomeNewSpec (I'll have to change the class that new specs extend every time I generate a new spec).
Am I asking too much? Short of modifying ObjectBehavior itself to look for and load an external custom matchers file, and making a pull request for the PHPSpec repo itself, is there a way to load custom matchers without getting into bad practices?
I've used an extension "phpspec-matcher-loader-extension" for this purpose -- it's worked well for me. From the description:
Allows custom phpspec matchers to be registered globally via configuration
There are two ways to get the result you want:
a) Extend ObjectBehaviour and add a new default getMatchers, then extend your new class instead of ObjectBehaviour. You can use a custom template to ensure that describe then generates classes extending the right object (see how PhpSpec itself uses a custom template https://github.com/phpspec/phpspec/tree/master/.phpspec)
b) Write your Matchers as objects implementing PhpSpec\Matcher\MatcherInterface then write an Extension that registers your custom matchers with PhpSpec. This is more complex and requires some understanding of how Matchers are registered.
We're thinking about how to make this easier in future releases, maybe via some configuration settings.
I've been using the Behat english-like test language (Gherkin?) to write test scripts but have quickly come up it's significant limitations.
If I could execute these tests in PHP within the phpunit test scripts that I have set up I could significant expand the tests that I could add. (I'm using FuelPHP).
I've been tinkering around for a few hours trying to get Behat to execute inside a PHPUNIT test script but have not had much luck.
Is this possible?
I think you are confusing something, because what you are saying doesn't make a lot of sense. If you having a hard time expressing the logic with the code, you should ask a specific question on that.
Behat and Mink are both written in PHP, you write your contexts in PHP, there is a gadzillion of plugins to make the life easier (also written in php). As the matter of fact, all your tests are executed in PHP when you run them… Yup!
If you want to compare data from two pages you can simply create a step like this:
/**
* #Then /^the page "(.+)" and the page "(.+)" content should somehow compare$/
*/
public function assertPageContentCompares($page1, $page2)
{
$session = $this->getSession();
$session->visit($page1);
$page1contents = $session->getPage()->getHtml();
$session->visit($page2);
$page2contents = $session->getPage()->getHtml();
// Compare stuff…
}
Besides the obvious, you can use PHPUnit in with Behat / Mink to make the assertions, i.e., in your step definitions. Most (in not all) PHPUnit assertions are static methods, using them is as simple as this:
PHPUnit_Framework_TestCase::assertSame("", "");
You can use Selenium (probably other frameworks too) with PHPUnit, if this is more about unit testing than functional testing, the official documentation tells how.
If you simply hate Gherkin then there's not much you can do with Behat – it's at the core of it. With PhpStorm 8 out there is a pretty good support for it, you can easily navigate around your code and refactor it quickly. If that doesn't cut it, there's another great alternative to Behat called Codeception, where you use pure PHP to define your tests. Maybe that's what you are looking for.
Yes. You can use the library I created: jonathanjfshaw/phpunitbehat.
Your phpunit tests will looks like this:
namespace MyProject\Tests;
use PHPUnit\Framework\TestCase;
use PHPUnitBehat\TestTraits\BehatTestTrait;
class MyTestBase extends TestCase {
use BehatTestTrait;
}
namespace MyProject\Tests;
class MyTest extends MyTestBase {
protected $feature = <<<'FEATURE'
Feature: Demo feature
In order to demonstrate testing a feature in phpUnit
We define a simple feature in the class
Scenario: Success
Given a step that succeeds
Scenario: Failure
When a step fails
Scenario: Undefined
Then there is a step that is undefined
FEATURE;
/**
* #Given a step that succeeds
*/
public function aStepThatSucceeds() {
$this->assertTrue(true);
}
/**
* #When a step fails
*/
public function aStepFails() {
$this->assertTrue(false);
}
}
I wrote a blog post explaining why I think this is not a bad idea.
I have a project with lot of files. Normally each file contains one class definition. Currently when I need an instance of object I use loader, which includes necessary file and instantiates it. Such approach though doesn't allow IntelliSense to work properly. And I prefer more readable new MyObject() than $loader->load("MyObject").
I use PHPStorm IDE. Is it possible to configure it to add necessary require_once("some_file.php") when I use appropriate class type?
Solution 1:
Switch to using the autoload feature of PHP (5+) and then use new MyObject():
http://www.php.net/manual/en/function.spl-autoload-register.php
Solution 2:
Use a live template defined like this:
/** #var $CLASSNAME$ $VARNAME$ **/
$VARNAME$ = $loader->load("$CLASSNAME$");
You can then choose the class name and the vriable name each time you use the livetemplate.
The feature to add use statements automatically will be available in PhpStorm 5.0.
If it's a specific project you could always create a template wich includes all you're crap! Ten when you do file-new it will give you a file based on that template.