Mock interface and trait simultaneously - php

Is it possible to build object like this (implementing interface and using a trait at the same time) in PHPUnit mock builder?
<?php
class FooClassThatD implements BarInterface
{
use BazTrait;
}

I don't think this is possible with the native mock object methods because they use a specific template to mock a class with a trait that doesn't allow any extension points. You can get around it easily with a test-specific class from which you build your mock.
abstract class BarWithBazTraitTestClass implements BarInterface
{
use BazTrait;
}
Create a mock for this class as you would any other abstract class.
$mock = $this->getMockForAbstractClass('BarWithBazTraitTestClass');

Related

How do i create a mock object that uses an array of traits?

I am currently on PHPUnit v5.7.27
I would like to create a mock object that uses an array of traits. How would I go about this? I only see getMockForTrait as a way to create a mock object using a single trait. My issue is that the trait requires the existence of another trait at the class level.
Update: More context to the issue
Given:
trait GetSet {
public function __call(){ /* does some magic */
}
trait RepositoryAware {
public function getRepository(string $name)
{
/* makes use of the GetSetTrait*/
}
}
class Controller
{
use GetSet;
use RepositoryAware;
}
Given the limitations of PHP, I can not simply put a use GetSet on the RepositoryAware trait because other traits that the controller imports could also bring the GetSet trait. Furhtermore, the controller class itself could be using the behavior provided by the GetSet trait.
The current solution I have is to create a Dummy Class that imports both traits and mock that class instead:
class RepositoryAwareClass
{
use GetSet;
use RepositoryAware;
}
Like this I am able to properly test the behavior provided by the RepositoryAware trait while at the same time composing its requirement of the GetSet trait.
Mocking concept was built with the idea that you would be using dependency injection. I can certainly see why you may not want to use dependency injection with this multiple inheritance like model that php uses called "Traits". Mocking tools like the one built for phpunit was built to substitute instances of objects not classes/interfaces/traits themselves. PHP Traits are more like having a static dependency instead of a dependency on an instance of an object. However, even if you were using traits and assuming a trait was basically the same as a class, according to mocking best practices should test the trait as its own test instead of testing a trait through another class. If you want to mock the trait itself you may want to try to revisit your design as I do not believe it can be done. You can certainly mock a trait and test that trait but you cannot mock a trait and then inject it as a dependency on an object. Imagine that a class for example implements an interface, mocking a trait would be the same a mocking an interface that a class implements, its not possible. You can only mock an interface of an object that a class depends upon through setter or constructor based dependency injection. Another example would be to try and mock the class that the class under test inherits from. You can't do that either. Perhaps in the javascript world this type of thing could be useful and from some people's point of view desired, but I think if you want to use mocking you would need to stick with object dependency injection instead of static use of traits.
So what's the alternative? I think the following example would be how to use perhaps "traditional" OOP practices with mocking to achieve your goal of sharing functionality without using inheritance. The example also makes your dependencies more explicit. And for the record, I applaud you for NOT using inheritance.
<?php
interface GetterSetter {
public function __call();
}
interface RepositoryProvider {
public function getRepository(string $name);
}
class GetSet implements GetterSetter {
public function __call() {
/* does some magic */
}
}
class DefaultRepository implements RepositoryProvider, GetterSetter {
/**
* #var GetterSetter
*/
private $_getterSetter;
public function __construct(GetterSetter $getterSetter) {
$this->_getterSetter = $getterSetter;
}
public function getRepository(string $name) {
// makes use of the GetSetTrait
$this->__call();
}
public function __call() {
// makes use of the GetSetTrait
$this->_getterSetter->__call();
}
}
class Controller implements RepositoryProvider, GetterSetter {
/**
* #var RepositoryProvider
*/
private $repositoryProvider;
public function __construct() {
$this->repositoryProvider = new DefaultRepository(new GetSet());
}
public function getRepository(string $name) {
return $this->repositoryProvider->getRepository($name);
}
public function __call() {
$this->repositoryProvider->__call();
}
}
In general I feel like the PHP community took a wild left turn, trying to be more like javascript and I feel that traits can walk you into a corner. This is a very good example of such a corner. I would really avoid them, at least for now. In the long run I believe Generics would be the better solution to your problem of needing a generic GetSet piece of code but generics haven't been implemented in php yet :-(.

PHPStorm 9 inspection of class inheritance works unexpectable

I'm facing the following issue in PHPStorm 9:
Say I have an interface FieldInterface that has some methods:
namespace Acme;
interface FieldInterface {
public function methodA();
public function methodB();
}
then I have an abstract class that implements base functionality of the interface. That abstract class has the user to implement certain methods, let's say it's methodB in our example:
namespace Acme;
abstract class AbstractField implements FieldInterface {
public function methodA() {
// implement methodA
}
public abstract function methodB(); // have the user implement it
}
And finally I have some ready-to-use class StringField:
namespace Acme;
class StringField extends AbstractField {
public function methodB() {
// implement methodB
}
}
At this point everything's going well. But if I add new method in the FieldInterface, PHPStorm does not say that anything is wrong with AbstractField while it's obvious that I should add public abstract function newMethod(); in there. However, it spots the error in StringField class instead.
It could be understood from the point that abstract classes are made for the purpose of extention, but usually you extend the abstract class rather than implement underlying interface. The whole meaning of making abstract class is to save user's time for implementing the interface. So why PHPStorm forces me to implement interface in concrete class rather than forcing me to implement it in abstract class that is explicitly implements the interface.
So I wonder if it is a bug in PHPStorm, or maybe it's done on purpose. Either way, is there any workaround?
That's how it should be, showing an error in the abstract class would be wrong.
In fact, public abstract function methodB(); is redundant because the abstract class already "inherits" this abstract method from the interface as it does not implement it.
The only workaround is to make AbstractField not abstract.

How to override a trait's method in abstract class?

I'm stuck into a problem with traits I can't solve on my own.
I have classes extending an abstract class (in my case these are several controller classes and an abstract class Controller, the used framework won't be important here, since this is a general PHP question…) that uses traits. I'd like to override a method defined in one of the traits. This only works as long as I define the method in my sub-classes but not in my abstract class.
So, this one works perfectly:
class MyController extends Controller
{
use AnyTrait;
public function anyMethodFromAnyTrait()
{
// override AnyTrait::anyMethodFromAnyTrait()
}
}
I also know how to call the anyMethodFromAnyTrait method from AnyTrait by using as.
class MyController extends Controller
{
use AnyTrait { AnyTrait::anyMethodFromAnyTrait as method }
public function anyMethodFromAnyTrait()
{
// invoke AnyTrait::anyMethodFromAnyTrait()
$this->method();
}
}
Both work like a charm.
But my problem is a bit different.
When using the trait and defining the method in my abstract class I am not able to override the trait's method.
Assume the following controller class:
class MyController extends Controller
{
public function anyAction()
{
// let's see what happens…
$this->anyMethodFromAnyTrait();
}
}
…and the abstract one that's extended by MyController:
abstract class Controller
{
use AnyTrait
public function anyMethodFromAnyTrait()
{
// do something different than AnyTrait
}
}
…And this is what's not working at all. Whenever I call $this->anyMethodFromAnyTrait() within MyController the trait's method as implememented in AnyTrait will be invoked. The same named method in my abstract Controller will be ignored.
Therefore I only can override a trait's method in a concrete sub-class but not in an abstract class that is extended by that sub-class.
So the method definitions in traits get a higher priority by PHP than the same method definitions in abstract classes.
Do you know any workaround for that behaviour?
One workaround would be to use the traits ONLY in the subclasses.
PHP always prefers the trait methods over the "local" ones.
The reason why it works in subclasses is, that the trait method of the superclass is extended, not the trait usage itself.

How use class interfaces when using PHPSpec

When testing with PHPSpec how can I use class interfaces injected into my methods rather then the actual concrete class?
For example I have a Product class that injects a VariationInterface into a method:
/**
* ...
*/
public function addVarient(VarientInterface $varient)
{
return $this->varients->add($varient);
}
Although since PHPSpec has no IOC container to bind VarientInterface to Varient I cant really test my classes.
Is it not best practice to code to an interface and not a concrete class?
You can mock concrete classes and intefaces in PHPSpec.
Please verify this example:
<?php
//file spec/YourNameSpace/ProductSpec.php
namespace spec\YourNameSpace\Product;
use YourNameSpace\VarientInterface;
use PhpSpec\ObjectBehavior;
class ProductSpec extends ObjectBehavior
{
function it_is_varients_container(VarientInterface $varient)
{
$this->addVarient($varient);
$this->getVarients()->shouldBe([$varient]);
}
}
You just pass VarientInterface as parameter to test method.
This VarientInterface is mocked underneath by PhpSpec (really by Prophecy).
Please check offical phpspec documentaion about mocking http://www.phpspec.net/docs/introduction.html#prophet-objects

PHP/Doctrine2 - Implement an interface twice

This might be a silly question. I am trying to create a generic repository interface for Doctrine 2 so I can pass it into my controller through direct injection:
//TestController.php
public function __construct(TestRepositoryInterface $p_repository){
//...
}
The method signature for an EntityRepository in Doctrine2 is as follows:
class EntityRepository implements ObjectRepository, Selectable{
//...
}
EntityRepository is missing a few functions that I would like to have in a repository(Add, Delete, Update). So I created a base repository interface and an abstract repository class to encapsulate those functions:
interface RepositoryInterface {
public function add($entity);
public function delete($entity);
public function update($entity);
}
The abstract repository class extends from EntityRepository so I can still get the functionalities of EntityRepository.
abstract class AbstractRepository extends EntityRepository{
public function add($entity){
//...
}
public function add($entity){
//...
}
public function add($entity){
//...
}
}
To tie everything together, I made TestRepositoryInterface extend from RepositoryInterface, ObjectRepository, and Selectable.
interface TestRepositoryInterface extends RepositoryInterface, ObjectRepository, Selectable{
}
Then I can just pass in an implementation of TestRepositoryInterface through direct injection:
class TestImplementation extends AbstractRepository implements TestRepositoryInterface{
//...
}
Or if I am unit testing, it would be easy to create a mock object or test stub.
My only concern is in TestImplementation class. It extends AbstractRepository which already implements ObjectRepository and Selectable (through EntityRepository), and at the same time TestImplementation also implements TestRepositoryInterface which also extends ObjectRepository and Selectable. So TestImplementation is essentially implementing ObjectRepository and Selectable twice (or is it?). It compiles just fine, but is this a valid approach?
It is perfectly OK for a class to implement multiple interfaces that in turn extend common interfaces. The methods are the same so there are no conflicts.
The only thing you need to worry about is implementing interfaces with same named methods but alternative arguments.
Say you have an interface whose implementations need to be iterated. You would probably have it implement\IteratorAggregate.
Now lets say your implementation class extends ArrayCollection (from doctrine common). Because ArrayCollection also implements IteratorAggregate it take care of some of your own interface's definitions for you.
When it comes to mixing interfaces, look for issues with compatibility rather than inheritance.

Categories