creating mock phpunit discrepency in mocked namespace and the original one - php

I have function like this:
class Bar{
public function a():Foo{
.
.
.
}
}
now I am trying to create a mock for the class Bar with php unit test
$mockedBar = $this->getMockBuilder(Bar::class)
->getMock()
->method('a')
->willReturn(new FakeFoo());
but when I am calling method a I am getting an error that method a return type must be instance of Foo not Mocked_blahblah.
unfortunately class Bar don't use any interface and the system is very big and I can't create an interface cause it make huge refactor in my codes;
is there any way to disable return type of function a in mocked object?
I am useing php7.2 and phpunit 6.0.13.
Here is a real scenario:
class A
{
public function b():B
{
echo "i am from class A function b";
}
}
class B
{
}
class FakeB
{
}
class ATest extends TestCase
{
public function testSayHi(){
$mockedA = $this->getMockBuilder(A::class)
->getMock();
$mockedA->method('b')->willReturn(new FakeB());
$mockedA->b();
}
}

You can't disable return types. Perhaps you could try to do it with some kind of a hackish error handler, but it's a crazy thing to do.
Good news is that you're not trying to do anything unusual and your tests can be fixed.
Firstly, you need to assign the result of getMock to a variable. Next, you can define your test double:
class MyTest extends TestCase
{
public function testIt()
{
$mockedBar = $this->getMockBuilder(Bar::class)->getMock();
$mockedBar
->method('a')
->willReturn(new FakeFoo());
$this->assertInstanceOf(Foo::class, $mockedBar->a());
}
}
This will only work if FakeFoo is of type of Foo, for example extends it:
class FakeFoo extends Foo
{
// override any Foo methods you'd like to fake
}
You don't need to create a Fake yourself, you can use PHPUnit to create a dummy:
class MyTest extends TestCase
{
public function testIt()
{
$mockedBar = $this->createMock(Bar::class);
$mockedBar
->method('a')
->willReturn($this->createMock(Foo::class));
$this->assertInstanceOf(Foo::class, $mockedBar->a());
}
}
To fix your second example:
class A
{
public function b():B
{
echo "i am from class A function b";
}
}
class B
{
}
class FakeB extends B
{
}
class ATest extends TestCase
{
public function testSayHi(){
$mockedA = $this->getMockBuilder(A::class)->getMock();
$mockedA->method('b')->willReturn(new FakeB());
$mockedA->b();
}
}
Or, instead of using a fake let phpunit handle it:
class ATest extends TestCase
{
public function testSayHi(){
$mockedA = $this->getMockBuilder(A::class)->getMock();
$dummyB = $this->createMock(B::class);
$mockedA->method('b')->willReturn($dummyB);
$mockedA->b();
}
}

Related

How can I overwrite a php function that has a argument type hint set?

I am trying to extend a package with my own functional ability. But the package code has type hints in the function calls, to other classes that are part of the package.
I am just looking for a way to modify the code.
More details about what I'm trying to do at
https://laracasts.com/discuss/channels/general-discussion/type-hint-hell
I have tried changing the code to use interfaces and abstracts, but i cant seem to prevent the "Declaration of class .... must be compatible with"error.
This is what i'm trying to do in a nutshell.
The package has this type of setup.
class ClassA {}
class ClassB {
public function makeClassA(ClassA $classA) : ClassA
{
return $classA;
}
}
This is what I am trying to do.
class ClassANew {}
class ClassC extends ClassB {
public function makeClassA(ClassANew $classA) : ClassANew
{
return $classA;
}
}
I get the following error,
"PHP Fatal error: Declaration of ClassC::makeClassA(ClassANew $classA): ClassANew must be compatible with ClassB::makeClassA(ClassA $classA): ClassA"
I know I could just fork the code and strip out the locked classA from ClassB, but I was trying not to do that.
If i was going to fork the code, I looked at how to maintain the premise of the original code. So, I tried changing the ClassA references in ClassB to a ClassAInterface, but I get the same error.
Is what I'm trying to do possible?
No, this is not possible to do.
Look here, for the reasons: Why is overriding method parameters a violation of strict standards in PHP?
this is a little trick, but its peculiarity that does not give rise to safety problems, in the past or already used and tested.
I know it's not really what you needed but it solves your problem to the full while maintaining the safety and the forcing of the returns of the methods
class ClassA {}
class ClassB {
public function makeClassA_ClassB(ClassA $classA) : ClassA
{
return $classA;
}
function __call($function_name, $argument){
if ($function_name==="makeClassA" && $argument[0] instanceof ClassA ) return $this->makeClassA_ClassB($argument[0]);
}
}
class ClassANew {}
class ClassC extends ClassB {
public function makeClassA_ClassC(ClassANew $classA) : ClassANew
{
return $classA;
}
function __call($function_name, $argument){
if ($function_name==="makeClassA" && $argument[0] instanceof ClassANew ) return $this->makeClassA_ClassC($argument[0]);
}
}
$t=new ClassC();
$t2=new ClassANew();
var_dump($t->makeClassA($t2)); // object(ClassANew)#212 (0) { }
$t=new ClassB();
$t2=new ClassA();
var_dump($t->makeClassA($t2)); // object(ClassA)#212 (0) { }
Ok, so I finally figured out the problem. I had to keep the original referenced return types the same. After that it works fine now.
namespace Original;
class ClassExtra {}
class ClassA {
public function __construct($container, ClassB $classB) {}
}
class ClassB {
public function __construct(ClassExtra $classExtra) {}
}
class ClassC {
public $classB;
public $containers;
public function __construct(ClassB $classB) {
$this->classB = $classB;
}
public function container(string $container = 'default'): ClassA
{
$this->containers[$container] = new ClassA($container, $this->classB);
return $this->containers[$container];
}
}
Namespace Changes;
Class NewClassA extends \Original\ClassA {}
Class NewClassB extends \Original\ClassB {}
Class NewClassC extends \Original\ClassC {
public function container(string $container = 'default'): \Original\ClassA
{
$this->containers[$container] = new NewClassA($container, $this->classB);
return $this->containers[$container];
}
}
$classC = new \Original\ClassC(new \Original\ClassB(new \Original\ClassExtra()));
var_dump(get_class($classC->container('test')));
/* string(15) "Original\ClassA" */
$classC = new NewClassC(new NewClassB(new \Original\ClassExtra()));
var_dump(get_class($classC->container('test')));
/* string(17) "Changes\NewClassA" */
That should be possible:
// just an example
class ClassA {} // the class within the used package
class ClassANew {} // your own class
// ClassB is the parent class, the one in the package
class ClassC extends ClassB {
public function makeClassA(ClassA|ClassANew $classA)
{
return $classA;
}
}
Make sure that each class/interface has correct inheritance.
More details: https://www.php.net/manual/en/language.oop5.variance.php
However, you MUST NOT totally change the logic, of the parent's function. There is an extend/implementation of the object for some reason.
If you want to just have a method that shares only the name, do not implement the new object as an child class of the previous class. The class should be only inherited from the previous class if it shares the purpose.
An example, of what I am trying to say:
There is no meaning to make a class 'Countie' that implements 'Countable' just to have a method 'count':
class Countie implements \Countable {
private int $num = 0;
public function count() {
foreach ($i = 1; $i <= $this->num; $i++) {
echo $i;
}
}
}

PHP - Protected/Abstract method to use class declared in child class

I have an abstract class, in which I want to call method, from a class that is declared in the child (extending) class. An example looks like this:
The abstract class:
abstract class NumberGenerator
{
protected function generate($input){
return MyClass::MyMethod($input);
}
}
My child/extending class:
use TomsFolder\MyClass;
use MyFolder\NumberGenerator;
class TomsNumberGenerator extends NumberGenerator
{
public function generate(string $applicantId): string
{
return $this->generate();
}
}
Another child/extending class:
use DavesFolder\MyClass;
use MyFolder\NumberGenerator;
class DavesNumberGenerator extends NumberGenerator
{
public function generate(string $applicantId): string
{
return $this->generate();
}
}
So I want to call MyClass::MyMethod in NumberGenerator. However it is only imported in TomsNumberGenerator.
The reason I want to do it like is because, I have classes like DavesNumberGenerator which calls a different MyClass.
When I try this, I get 'MyClass is not found in NumberGenerator'. Is there any way to make this work?
Try putting the namespace use statement before the actual class:
NumberGenerator.php
use MyFolder\MyClass;
abstract class NumberGenerator
{
protected function generate($input){
return MyClass::MyMethod($input);
}
}
EDIT
Try this:
NumberGenerator.php
abstract class NumberGenerator
{
protected function generate($class_name, $input){
return call_user_func($class_name . '::MyMethod', $input);
}
}
TomsNumberGenerator.php
use TomsFolder\MyClass;
use MyFolder\NumberGenerator;
class TomsNumberGenerator extends NumberGenerator
{
public function generate(string $applicantId): string
{
return $this->generate(get_class(new MyClass()), $applicantId);
}
}
You have to use interface for this.
You can do the following
Create MyClassInterface
interface MyClassInterface {
public function MyMethod();
}
Implement this interface in some classes
class MyClass1 implements MyClassInterface {
public function MyMethod() {
// implementation
}
}
class MyClass2 implements MyClassInterface {
public function MyMethod() {
// implementation 2
}
}
Add abstract method to NumberGenerator
abstract class NumberGenerator {
abstract protected function GetMyClass(): MyClassInterface;
protected function generate($input){
return $this->GetMyClass()->MyMethod($input);
}
}
Implement GetMyClass function inside child classes
class TomsNumberGenerator extends NumberGenerator
{
protected function GetMyClass(): MyClassInterface {
return new MyClass1();
}
}
class DavesNumberGenerator extends NumberGenerator
{
protected function GetMyClass(): MyClassInterface {
return new MyClass2();
}
}
PS If you want to use static, you can change abstract inside NumberGenerator class, to change string. In this case, your generate will look like this:
protected function generate($input){
return call_user_func($this->GetMyClass() . '::MyMethod', [$input]);
}

Testing overriden trait method execution

I have situation like this. I have some 3rd party trait (I don't want to test) and I have my trait that uses this trait and in some case runs 3rd party trait method (in below example I always run it).
When I have code like this:
use Mockery;
use PHPUnit\Framework\TestCase;
class SampleTest extends TestCase
{
/** #test */
public function it_runs_parent_method_alternative()
{
$class = Mockery::mock(B::class)->makePartial();
$class->shouldReceive('fooX')->once();
$this->assertSame('bar', $class->foo());
}
protected function tearDown()
{
Mockery::close();
}
}
trait X {
function foo() {
$this->something->complex3rdpartyStuff();
}
}
trait Y2 {
function foo() {
$this->fooX();
return 'bar';
}
}
class B {
use Y2, X {
Y2::foo insteadof X;
X::foo as fooX;
}
}
it will work fine however I don't want code to be organized like this. In above code in class I use both traits but in code I want to test in fact trait uses other trait as mentioned at the beginning.
However when I have code like this:
<?php
use Mockery;
use PHPUnit\Framework\TestCase;
class SampleTest extends TestCase
{
/** #test */
public function it_runs_parent_method()
{
$class = Mockery::mock(A::class)->makePartial();
$class->shouldReceive('fooX')->once();
$this->assertSame('bar', $class->foo());
}
protected function tearDown()
{
Mockery::close();
}
}
trait X {
function foo() {
$this->something->complex3rdpartyStuff();
}
}
trait Y {
use X {
foo as fooX;
}
function foo() {
$this->fooX();
return 'bar';
}
}
class A {
use Y;
}
I'm getting:
undefined property $something
so it seems Mockery is not mocking in this case X::foo method any more. Is there are way to make possible to write such tests with code organized like this?
So far it is not possible to mock deeper aliased methods. You can proxy aliased method call using local method and allowing mocking protected methods.
Check code below
use Mockery;
use PHPUnit\Framework\TestCase;
class SampleTest extends TestCase
{
/** #test */
public function it_runs_parent_method()
{
$mock = Mockery::mock(A::class)->shouldAllowMockingProtectedMethods()->makePartial();
$mock->shouldReceive('proxyTraitCall')->once();
$this->assertSame('bar', $mock->foo());
}
protected function tearDown()
{
Mockery::close();
}
}
trait X {
function foo() {
$this->something->complex3rdpartyStuff();
}
}
trait Y {
use X {
foo as fooX;
}
function foo() {
$this->proxyTraitCall();
return 'bar';
}
function proxyTraitCall() {
return $this->fooX();
}
}
If you autoload trait you can try to overload it using Mockery.
/** #test */
public function it_runs_parent_method()
{
$trait = Mockery::mock("overload:" . X::class);
$trait->shouldReceive('foo')->once();
$class = Mockery::mock(A::class)->makePartial();
$this->assertSame('bar', $class->foo());
}
Don't test implementation details. Test it like you use it.
Class user have to know only public interface to use it, why test should be any different?
Fact that one internal method call different one is implementation detail and testing this breaks encapsulation. If someday you will switch from trait to class method without changing class behaviour you will have to modify tests even though class from the outside looks the same.
From Pragmatic Unit Testing by Dave Thomas and Andy Hunt
Most of the time, you should be able to test a class by exercising its
public methods. If there is significant functionality that is hidden
behind private or protected access, that might be a warning sign that
there's another class in there struggling to get out.

Php: Overriding abstract method goes wrong

I think there is a problem in php's OOP implementation.
EDIT: Consider more illustrative example:
abstract class Animal {
public $name;
// public function Communicate(Animal $partner) {} // Works
public abstract function Communicate(Animal $partner); // Gives error
}
class Panda extends Animal {
public function Communicate(Panda $partner) {
echo "Hi {$partner->name} I'm a Panda";
}
}
class Human extends Animal {
public function Communicate(Human $partner) {
echo "Hi {$partner->name} I'm a Human";
}
}
$john = new Human(); $john->name = 'John';
$mary = new Human(); $mary->name = 'Mary';
$john->Communicate($mary); // should be ok
$zuzi = new Panda(); $zuzi->name = 'Zuzi';
$zuzi->Communicate($john); // should give error
The problem is that when Animal::Communicate is an abstract method, php tells that the following methods are illegal:
"public function Communicate(Panda $partner)"
"public function Communicate(Human $partner)"
but when Animal::Communicate is non-abstract but has zero-implementation Php thinks that these methods are legal. So in my opinion it's not right because we are doing override in both cases, and these both cases are equal, so it seems like it's a bug...
Older part of the post:
Please consider the following code:
Framework.php
namespace A
{
class Component { ... }
abstract class Decorator {
public abstract function Decorate(\A\Component $component);
}
}
Implementation.php
namespace B
{
class MyComponent extends \A\Component { ... }
}
MyDecorator.php
namespace A
{
class MyDecorator extends Decorator {
public function Decorate(\B\MyComponent $component) { ... }
}
}
The following code gives error in MyDecorator.php telling
Fatal error: Declaration of MyDecorator::Decorate() must be compatible with that of A\Decorator::Decorate() in MyDecorator.php on line ...
But when I change the Framework.php::Decorator class to the following implementation:
abstract class Decorator {
public function Decorate(\A\Component $component) {}
}
the problem disappears.
I'm not sure (haven't tested it ;), but you declare this abstract function:
public abstract function Decorate(\A\Component $component);
So you should implement this EXACTLY like that. But you did this:
public function Decorate(\B\MyComponent $component) { ... }
That's not the same. Could you try to change that to \A\Component?
To all comments: fact of the matter is that this piece of PHP "runs"
namespace A
{
class Component { }
abstract class Decorator {
public abstract function Decorate(\A\Component $component);
}
}
namespace B
{
class MyComponent extends \A\Component { }
}
namespace A
{
class MyDecorator extends Decorator {
public function Decorate(\A\Component $component) {}
}
}
And this doesn't:
<?php
namespace A
{
class Component { }
abstract class Decorator {
public abstract function Decorate(\A\Component $component);
}
}
namespace B
{
class MyComponent extends \A\Component { }
}
namespace A
{
class MyDecorator extends Decorator {
public function Decorate(\B\MyComponent $component) {}
}
}
?>
With this error: PHP Fatal error: Declaration of A\MyDecorator::Decorate() must be compatible with that of A\Decorator::Decorate() in line 18
Now you can discuss all you like about how that should or should not be, but that's the problem with the code.
so, to satisfy my own curiosity: this is illegal too:
<?php
class Component { }
abstract class Decorator {
public abstract function Decorate(Component $component);
}
class MyComponent extends Component { }
class MyDecorator extends Decorator {
public function Decorate(MyComponent $component) {}
}
?>
It's not the namespaces or anything. It just doesn't seem legal.
See http://bugs.php.net/bug.php?id=36601, this issues has been reported as a bug but was rejected because of laziness :D
It has nothing to do with it being abstract. It has to do with the type hinting. The two definitions of the method are not compatible because you explicitly set the argument to be of type \A\Component and then try to overload the method with \B\Component you cant do that because it changes the method signature. Any subsequent declaration of Decorate must use the same type hint as its parent declaration in order for the method signatures to be compatible.
This might assist someone, and am not late.
The best way to handle such is by using an interface.
Consider below;
<?php
interface Componentor{}
class Component implements Componentor { }
abstract class Decorator {
public abstract function Decorate(Componentor $component);
}
class MyComponent extends Component { }
class MyDecorator extends Decorator {
public function Decorate(Componentor $component) {}
}
?>
Usage;
<?php
$c=new Component();
//TODO ....blah blah blah...
$decor=new MyDecorator();
//TODO ....blah blah blah...
$decor->Decorate($c);
?>

PHPUnit Stubbing Class methods declared as "final"

I'm writing a unit test for a class method that calls another class's method using a mock, only the method that needs to be called is declared as final, so PHPUnit is unable to mock it. Is there a different approach I can take?
example:
class to be mocked
class Class_To_Mock
{
final public function needsToBeCalled($options)
{
...
}
}
my test case
class MyTest extends PHPUnit_Framework_TestCase
{
public function testDoSomething()
{
$mock = $this->getMock('Class_To_Mock', array('needsToBeCalled'));
$mock->expects($this->once())
->method('needsToBeCalled')
->with($this->equalTo(array('option'));
}
}
Edit: If using the solution provided by Mike B and you have a setter/getter for the object you're mocking that does type checking (to ensure the correct object was passed into the setter), you'll need to mock the getter on the class you're testing and have it return the other mock.
example:
class to be mocked
class Class_To_Mock
{
final public function needsToBeCalled($options)
{
...
}
}
mock
class Class_To_MockMock
{
public function needsToBeCalled($options)
{
...
}
}
class to be tested
class Class_To_Be_Tested
{
public function setClassToMock(Class_To_Mock $classToMock)
{
...
}
public function getClassToMock()
{
...
}
public function doSomething()
{
$this->getClassToMock()
->needsToBeCalled(array('option'));
}
}
my test case
class MyTest extends PHPUnit_Framework_TestCase
{
public function testDoSomething()
{
$classToTest = $this->getMock('Class_To_Be_Tested', array('getClassToMock'));
$mock = $this->getMock('Class_To_MockMock', array('needsToBeCalled'));
$classToTest->expects($this->any())
->method('getClassToMock')
->will($this->returnValue($mock));
$mock->expects($this->once())
->method('needsToBeCalled')
->with($this->equalTo(array('option'));
$classToTest->doSomething();
}
}
I don't think PHPUnit supports stubbing/mocking of final methods. You may have to create your own stub for this situation and do some extension trickery:
class myTestClassMock {
public function needsToBeCalled() {
$foo = new Class_To_Mock();
$result = $foo->needsToBeCalled();
return array('option');
}
}
Found this in the PHPUnit Manual under Chapter 11. Test Doubles
Limitations
Please note that final, private and static methods cannot be stubbed or mocked. They are ignored by PHPUnit's test double functionality and retain their original behavior.
I just stumbled upon this issue today. Another alternative is to mock the interface that the class implements, given that it implements an interface and you use the interface as type hinting.
For example, given the problem in question, you can create an interface and use it as follows:
interface Interface_To_Mock
{
function needsToBeCalled($options);
}
class Class_To_Mock implements Interface_To_Mock
{
final public function needsToBeCalled($options)
{
...
}
}
class Class_To_Be_Tested
{
public function setClassToMock(Interface_To_Mock $classToMock)
{
...
}
...
}
class MyTest extends PHPUnit_Framework_TestCase
{
public function testDoSomething()
{
$mock = $this->getMock('Interface_To_Mock', array('needsToBeCalled'));
...
}
}

Categories