How to create global custom matchers in PHPSpec? - php

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.

Related

Expand / Override parent's method in constructor filter

I'm working on a WordPress project. There is a child theme implemented, and the parent one has a class to show authors' links. We have implemented a feature to support multiple authors per post, then we have our own methods for showing the proper links (not a single-author link, but many links joined depending on the authors count).
The complete scenario:
Theme: Newspaper
File: td_module.php (includes/wp_booster/td_module.php)
Class: td_module (abstract, many other classes in the parent theme inherit this one)
Method: get_author()
There are many other classes inheriting td_module in the parent theme, hence I cannot just extend td_module in my child theme since every change to the parent theme would be lost when the theme gets updated
Somehow I need to expand this method to show something different, but I don't want to change the method in the td_module class: even when it would be the fastest / most secure solution, this code would be overwritten on every theme upgrade.
This method is widely used in the Theme, that's why is that important to add some code here.
Why I still have some hope: There is a WP filter td_wp_booster_module_constructor, being called on the constructor of td_module class. The problem is that this class doesn't have any other filter on the method for showing the authors, but just the td_wp_booster_module_constructor filter call on the constructor.
The base PHP (and OOP) question: is it possible to expand / replace a method by using this filter in the constructor somehow?
Some code to clarify:
abstract class td_module {
...
function __construct($post, $module_atts = array()) {
...
// Can I change the get_author behavior by using this filter?
apply_filters("td_wp_booster_module_constructor", $this, $post);
// This is the only filter available in the entire class!
...
}
...
function get_author() {
$buffy = '';
// Code for generating author link ($buffy .= ...)
...
// This function doesn't have any apply_filter, there are no filters available
return $buffy;
}
}
Basically, you can't modify a class definition at runtime. The fact that you have a filter available on the constructor is a red herring and really not relevant.
Either the get_author() has some facility to change it's behaviour from outside class definition (in Wordpress parlance, filter and action hooks), or you simply cannot do it.
Outside of Wordpress, in an application with a proper dependency inversion container, you should probably do this by decorating the class and having all class consumer use the decorated class.
But since you have no way of telling class consumers to use one or the other, and a lot of code out of your control is presumably instantiating the class directly, something like this simply won't fly.
There is one extension, Runkit, that allows for changes of behaviour and definitions at runtime. there is even a method to modify a method definition. although I have not tried the extension, and do not know if it runs in an updated PHP runtime.
Note it is almost certainly a very bad idea to do this on production code, and that since you are dealing with code out of your control anyway you can't even be sure you would be changing the definition before it's used for the first time.
I've also found a repo for Runkit that says that it almost works on PHP 7, if you are absolutely convinced on going that way.
Your issue is that the base class is vendor code which you did not write.
So create a class in between your classes extending it!
Job done!
<?php
class SomeWordpressCrap
{
public function doSomething()
{
return 'something';
}
}
class YourAwesomeNewClass extends SomeWordpressCrap
{
public function doSomething()
{
return 'something better!';
}
}
class OneOfYourExistingClasses extends YourAwesomeNewClass
{
}
UPDATE So it turns out the OneOfYourExistingClasses is also vendor code, so the above solution will not work.
However! You could use Roave's "Better Reflection" lib, which you can find here https://github.com/Roave/BetterReflection
This will allow you to "Change the body of a function or method to do something different", which I believe is exactly what you need. Good luck!

Adding interface to mock with Mockery (hard dependency)

I need to mock CurrencyEnum by overload it, but it's not the end becouse i need to add interface to this mock.
This doesn't work:
Mockery::mock('overload:'.CurrencyEnum::class);
Error: (..) must be an instance of \BaseCurrency, instance of \CurrencyEnum given.
I looked at Mockery\Container::mock and I dont't have idea how to do it.
In example I want to test TestingClass::first() method
class CurrencyEnum implements BaseCurrency
{
/* methods */
}
class TestingClass
{
public function first(string $currencySymbol)
{
$abc = 'some_string';
return $this->second($abc, new CurrencyEnum($currencySymbol));
}
private function second(string $abc, BaseCurrency $currency)
{
/* code */
}
}
The overload method works by intercepting the autoload mechanism: it registers an autoloader for the overloaded class, loading the mocked version of the class instead of the original.
By default, it does not add many things to the mocked class. You can, however, configure just about anything you may need.
Usually, implementing one or more interfaces can be done by providing a comma-separated list of fully qualified names, the first one being the class:
$mock = Mockery::mock('MyClass, MyInterface, OtherInterface');
Due to the way that the Mockery::mock method is set up, this will not work. (The author apologises in the source code)
However, we can pass the interface(s) as second argument to the mock method:
Mockery::mock('overload:'.CurrencyEnum::class, BaseCurrency::class);
This will cause the MockConfigurationBuilder to add BaseCurrency as target; since it's an interface it will make the mock implement the interface.
An alternative notation of the above would be to use the builder directly:
Mockery::mock(
(new MockConfigurationBuilder())
->setInstanceMock(true)
->setName(CurrencyEnum::class)
->addTarget('stdClass')
->addTarget(BaseCurrency::class)
)
Having said that, it's a notoriously bad practice to mock things like enums and value objects. Why not just use the actual CurrencyEnum? Something as simple as a currency code does not quite warrant mocking at all. There's probably a structural improvement to make, which would simultaneously add tons of value to your tests and make them simpler to read.

Is it possible to use PHPUnit assertions without extending PHPUnit_Framework_TestCase?

Maybe the question comes across as weird, but here is the problem that I'm trying to solve... first of all, please keep in mind I am more of a Java developer getting used to working with PHP, so maybe my thought process is the problem!
I am testing a web site that I have built with Symfony. For my component testing, I create my test class extending WebTestCase and my test I have a set of assertions like the following to verify that the page title is where I want it and containing what I expect:
$text = "Page Title";
$selector = "h2#pageHeading";
$this->assertEquals(1, $crawler->filter($selector)->count(), "Found wrong number of elements using selector ".$selector);
$this->assertEquals(trim($text),
trim($crawler->filter($selector)->text()),
"Element $selector didn't have expected text");
Then I write more tests for other pages within the site, and in all of them I want to test the title is there as it should be, so to be able to reuse code I refactor the above into a function in parent class that the other test classes reuse:
function assertPageTitle($text) {
$selector = "h2#pageHeading";
$this->assertEquals(1, $crawler->filter($selector)->count(), "Found wrong number of elements using selector ".$selector);
$this->assertEquals(trim($text),
trim($crawler->filter($selector)->text()),
"Element $selector didn't have expected text");
}
And I call that method in my tests. As tests develop there are more similar "complex assertions" that I can refactor, and all of them go to the parent class, thus bloating my parent class into a massive assertion container:
protected function assertSelectedOptionContainsTextValue($selector, $text, $value, $crawler) {
...
}
protected function assertMenusContainItems($menus, $crawler) {
...
}
protected function assertErrorMessageShown($message, $crawler) {
...
}
... (more and more) ...
You get the idea. My next thought at this point is to refactor all this "complex assertions" into other classes, probably following the Page Object pattern, but then the other classes won't have access to the assertEquals method unless those other classes also extend WebTestCase or at least PHPUnit_Framework_TestCase, which doesn't seem a very good idea...
So is there an easy way to access the assertEquals method (and related) without having to extend the base PHPUnit classes? Can I use composition somehow?
PHPUnit's built-in assertions are implemented as public static methods in the PHPUnit_Framework_Assert class. Just invoke them as PHPUnit_Framework_Assert::assertTrue(), for instance.
First of all, you are overwriting $text variable at the beginning of your "generic" function - but I understand that you want to only show idea.
Secondly, using unit testing to this kind of tests isn't the best choose. I think you should extract logic of titles to separated classes and then test it by PHPUnit or use more appropriate testing solutions like Behat or Selenium.
If you still haven't changed your opinion... You must extend at least PHPUnit_Framework_TestCase whenever you want to use PHPUnit to testing.
Little hint: Traits would be good to extract your assertions.
In order to create reusable phpunit assertion in trait. That can be used in many tests cases. You can use trait that statically calls PHPUnit_Framework_Assert::assertSame, quick example how to achieve it:
<?php
namespace Tests\Foo;
use PHPUnit_Framework_Assert;
trait AssertTrait
{
public function assertInTraitAlwaysFailing()
{
PHPUnit_Framework_Assert::assertSame(1, 2, 'always failing');
}
}
It would act as helper that can be used in many places where you need same assertion to prevent duplication.

CakePHP - passing constructor arguments to custom components

I am trying to use a custom class in cakephp. Initially I had created a vendor class which works fine but I can't use other cakephp components.
To use built in components like $this->Text, I can create a custom component but the constructor requires a argument which is a json object returned from an API and I need to keep initializing in a loop
//The constructor for the class
function __construct($objValue) {
$this->messageId = $objValue['id'];
Is using a component suitable for this purpose?
you don't need to create a component here if you don't need it in the controller scope.
also you don't need to make it a vendor class (which is third party stuff).
cake offers you a way out: Libs in APP/Lib
You can use them anywhere anytime.
App::uses('MyClassName', 'Lib');
$MyClass = new MyClassName();
You might even want to create a package in Lib itself - e.g. "Lib/Utility":
App::uses('MyClassName', 'Utility');
without knowing any more about what exactly this custom class does, it is difficult to be any more specific here.

How to generate or modify a PHP class at runtime?

The schmittjoh/cg-library seems what I need, but there is no documentation at all.
This library provides some tools that you commonly need for generating
PHP code. One of it's strength lies in the enhancement of existing
classes with behaviors.
Given A class:
class A {}
I'd like to modify, at runtime of course and with some cache mechanism, class A, making it implementing a given interface:
interface I
{
public function mustImplement();
}
... with a "default" implementation for method mustImplement() in A class.
You can also use a Role Object pattern and good old aggregation.
Instead of having smart Entities that contain all your business logic, you make them dumb and move all the business logic into Roles that aggregate the dumb Entities then. Your behaviors are then first-class citizens living inside the Roles.
Example:
interface BannableUser
{
public function ban();
}
Having an interface with one particular behavior follows the Interface Segregation Principle. It also dramatically increases possible reuse since you are more likely to reuse individual behaviors than an Entity with an application-specific collection of behaviors.
Now to implement that, you create an appropriate Role Class:
class BannableUserRole implements BannableUser
{
private $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function ban()
{
$this->user->isBanned = true;
}
}
You still have a User entity but it's completely stripped of all behaviors. It's essentially just a bag of Getters and Setters or public properties. It represents what your System is. It's the data part, not the interaction part. The interaction is inside the Roles now.
class User
{
public $isBanned;
// … more properties
}
Now assuming you have some sort of Web UI, you can do the following in your controller:
class BanUserController implements RequestHandler
{
// …
public function handleRequest(Request $request)
{
$userId = $request->getVar('user_id');
$user = $this->userRepository->findById($userId);
$bannableUser = new BannableUserRole($user);
$bannableUser->ban();
}
}
You can decouple this further by moving the actual lookup of the User and assignment of the Role into a UseCase class. Let's call it Context:
class BanUserContext implements Context
{
public function run($userId)
{
$user = $this->userRepository->findById($userId);
$bannableUser = new BannableUserRole($user);
$bannableUser->ban();
}
}
Now you have all the business logic inside your Model layer and fully isolated from your User Interface. The Contexts are what your system does. Your Controller will only delegate to the appropriate Context:
class BanUserController implements RequestHandler
{
// …
public function handleRequest(Request $request)
{
$this->banUserContext->run($request->getVar('user_id'));
}
}
And that's it. No need for Runkit or similar hackery. The above is a simplified version of the Data Context Interaction architectural pattern, in case you want to further research this.
Note: OP needs PHP 5.3 (it was not tagged that way before), this question is a general outline for PHP 5.4.
You can do that with defining the interface and adding traits that contain the default implementation for those interfaces.
Then you create a new class definition that
extends from your base-class,
implements that interface and
uses the default traits.
For an example, see Traits in PHP – any real world examples/best practices?
You can easily generate that class definition code and either store it and include it or eval it straight ahead.
If you make the new classname containing all the information it consists of (in your case the base classname and the interface), you can prevent to create duplicate class definitions easily.
This works without any PHP extension like runkit. If you bring serialize into the play, you can even overload existing objects at runtime with the new interface in case they can serialize / deserialize.
You can find a code-example that has been implementing this in:
DCI-Account-Example-in-PHP / src / DCI / Casting.php (usage)
I needed a tool to edit PHP classes (specifically Doctrine Entities), I could not find it so I created one that might help you, I documented it heavily. So if you want to give it a try, I'll happily help.
Here it is, SourceEditor
It gives you an API like this:
/* #var $classEditor DocDigital\Lib\SourceEditor\PhpClassEditor */
$classEditor->parseFile($classPath);
$classEditor->getClass('a')->getMethod('b')->addAnnotation('#auth Juan Manuel Fernandez <juanmf#gmail.com>');
$classEditor->getClass('a')->getAttribute('attr')->addAnnotation('#Assert\Choice(...)');
$classEditor->getClass('a')->addAttribute($attr2);
$classEditor->getClass('a')->addUse('use DocDigital\Bundle\DocumentBundle\DocumentGenerator\Annotation as DdMapping;');
$classEditor->getClass('a')->addConst(' const CONSTANT = 1;');
file_put_contents($classPath, $classEditor->getClass('a')->render(false));
Runkit extension can help you
Runkit_Sandbox — Runkit Sandbox Class -- PHP Virtual Machine
Runkit_Sandbox_Parent — Runkit Anti-Sandbox Class
runkit_class_adopt — Convert a base class to an inherited class, add ancestral methods when appropriate
runkit_class_emancipate — Convert an inherited class to a base class, removes any method whose scope is ancestral
runkit_constant_add — Similar to define(), but allows defining in class definitions as well
runkit_constant_redefine — Redefine an already defined constant
runkit_constant_remove — Remove/Delete an already defined constant
runkit_function_add — Add a new function, similar to create_function
runkit_function_copy — Copy a function to a new function name
runkit_function_redefine — Replace a function definition with a new implementation
runkit_function_remove — Remove a function definition
runkit_function_rename — Change the name of a function
runkit_import — Process a PHP file importing function and class definitions, overwriting where appropriate
runkit_lint_file — Check the PHP syntax of the specified file
runkit_lint — Check the PHP syntax of the specified php code
runkit_method_add — Dynamically adds a new method to a given class
runkit_method_copy — Copies a method from class to another
runkit_method_redefine — Dynamically changes the code of the given method
runkit_method_remove — Dynamically removes the given method
runkit_method_rename — Dynamically changes the name of the given method
runkit_return_value_used — Determines if the current functions return value will be used
runkit_sandbox_output_handler — Specify a function to capture and/or process output from a runkit sandbox
runkit_superglobals — Return numerically indexed array of registered superglobals
You can look: https://github.com/ptrofimov/jslikeobject
Author implemented dynamic JS-like objects with support of inheritance.
But it is more like a joke than real proposition.

Categories