PHPUnit: stub methods undefined - php

I must be missing something. I'm trying to stub methods on a class in PHPUnit, but when I invoke the method on the mock object, it tells me that method is undefined.
Example class to stub:
namespace MyApp;
class MyStubClass
{
public function mrMethod()
{
// doing stuff
}
}
To stub it, I write:
// specifying all getMock() args to disable calling of class __construct()
$stub = $this->getMock('MyStubClass', array(), array(), 'MockMyStubClass', false, false, false);
$stub->expects($this->any())
->method('mrMethod')
->will($this->returnValue('doing stuff'));
But upon invoking the stubbed method, I get an exception:
$stub->mrMethod();
//PHP Fatal error: Call to undefined method MockMyStubClass::mrMethod()
I'm using PHPUnit 3.4.3 with PHP 5.3.0.
Another small thing I noticed was that if specifying a namespace in the getMock() method results in a class loading exception because of a double namespace:
$stub = $this->getMock('MyApp\MyStubClass');
// Fatal error: Class 'MyApp\MyApp\MyStubClass' not found
That strikes me as rather odd (and getmock() will not accept a namespace with a leading backslash). The only thing I could think to cause that would may be because this class is
registered with an autoloader?
Any thoughts?

Answering my own question:
After quite a bit of frustration, I did manage to get things working. I'm not sure precisely what the issue was, but did discover a few things that might help others:
Make sure you're running the latest version of PHPUnit (3.4.6 as of this writing)
Use the fully-qualified namespace minus the first backslash.
$this->getMock('MyApp\Widgets\WidgetFactory');
Part of my problem was that PHPUnit was creating a stub class WidgetFactory that was not actually stubbing MyApp\Widgets\WidgetFactory. One would expect that an exception would occur if trying to stub a non-existent class, but it doesn't seem to happen with the namespace confusion.
Also, there is a question over here that suggests using the class alias method as follows:
class_alias('MyApp\Widgets\WidgetFactory', 'WidgetFactory');
$this->getMock('WidgetFactory');
While this did temporarily solve my problem, I would strongly advise against using it. class_alias() cannot be called twice for the same alias without raising an exception, which causes obvious problem if used in the setup() method, or as part of the stub generation.

I had a similar issue, my problem was that the path I was writing was something like MyApp\Widgets\WidgetFactory\MyStubClass while the class was something like this:
namespace MyApp;
class MyStubclass
{
public function mrMethod()
{
// doing stuff
}
}
So there wasn't the uppercase C in the name of the class in the path

Related

Mock an Exception in phpUnit

The method I am trying to test has a try catch like
try {
$fooClass->doStuff(); // Throws \Lib\Custom\Exception
}
catch (\Lib\Custom\Exception $exception) {
return false;
}
I want to test if the return is false, but the custom exception is not loaded when my tests are executed.
Php unit has the option of mocking classes, but I can't seem to use this for Exceptions.
$exceptionMock= $this->getMockBuilder(\Lib\Custom\Exception::class)->getMock();
$fooClassMock = $this->getMockBuilder(fooClass::class)->getMock()
->method('doStuff')
->willThrowException($exceptionMock);
Gives me the folowing exception:
Argument 1 passed to
PHPUnit_Framework_MockObject_Builder_InvocationMocker::willThrowException()
must be an instance of Exception, instance of Mock_Exception_c4dd9394 given
How to properly mock this Exception to test the function?
I don't think you need to mock the exception.
why not try this?
$fooClassMock = $this->getMockBuilder(fooClass::class)->getMock()
->method('doStuff')
->willThrowException(new \Lib\Custom\Exception());
Or something similar..
The reason that your test isn't working is because the class isn't known in the test. When that happens and you create a mock in PHPUnit, it does some trickery to create a class definition and then extends that to create the mock. Otherwise it would use the extend the class itself and you would not have had a problem.
In this case, PHPUnit creates a fake \Lib\Custom\Exception and then uses that to create the mock. The fake class created doesn't extend anything (since it wouldn't know what to extend/implement). So the type hint in the willThrowException will not be matched because your mock object didn't extend Exception.
This would happen with any type hinting for extended classes in PHPUnit if the class is not loaded in the test. In order to fix this, you need to have the class available in the test via a require, include, or autoloader.

Interface and Traits does not work properly

This is some weird behaviour I witnessed today and I wonder why.. I`ll get straight to the point:
So we are using a2lix translations with symfony and fos elasticabundle
class Class
{
use Translatable;
}
class ClassTranslation implements \A2lix\I18nDoctrineBundle\Doctrine\Interfaces\OneLocaleInterface
{
use Translation;
}
In this scenario once I run
php app/console fos:elastica:populate
I`m getting:
[InvalidArgumentException] Parameter 'locale' does not
exist.
But the parameter is actually in the trait(from the vendor that include 2 traits translatable methods and translatable properties)
The questions is why does this happen. We have the property from the trait yet once we run the command it does not find it.
Ok now here comes the strange part!
In this scenario everything works as expected:
class Class
{
use Translatable;
}
class ClassTranslation
{
use Translation;
}
If I decide not to implement the interface the command runs smoothly, the interface just forces you to have getter and setter for the locale atribute. I just wonder why does this happen.
This may relates to https://github.com/a2lix/I18nDoctrineBundle/issues/16.
I don't think this is related to php traits since there wouldn't be an InvalidArgumentException. Maybe the code of the trait is not executed if the interface is not present, so there is no error in this case. Please check the implementation of your trait, maybe do some debug output to see when the code is executed and when the exeption occurs.

How to unset namespace in php to access global class

I have a lot of SDKs which have the same class names so I have moved them into folders and created namespaces. These sdks are generally used to access APIs.
//directory e.g. vendor/company/sdk/files
vendors/ebat/feeback/environment.php
I then have completely separate libraries which I have created myself which contain any methods I need to interact with these sdks. also namespaced.
//directory e.g.
library/marketplace/ebay/feedback.php
Not sure what the correct terminology is but within these libraries are some global objects e.g. stdClass and ReflectionClass which a cant seem to access/find.
feedback.php
namespace Marketplace\Ebay;
class Feedback extends \Ebat\Feedback\EbatNsFeedback_Environment {
//simple example
public function new_ReflectionClass($obj){
return new ReflectionClass($obj);
}
}
Now if I were to try and call this method
$this->marketplace->ebay->feedback->new_ReflectionClass(new stdClass()));
this error is shown.
Fatal error: Class 'Marketplace\Ebay\ReflectionClass' not found
When I add a backslash
return new \ReflectionClass($obj);
it then gives me this error
Fatal error: Class 'Ebat\Feedback\ReflectionObject' not fount
How can I access the native/global ReflectionClass class, all similar questions seem to be fixed by adding a backslash.
Adding a leading \ is the correct way to access top-level namespaces/classes.
But it seems you're running in to a similar error elsewhere - note that your 2nd fatal error is about ReflectionObject, not ReflectionClass. Do you have other code that tries to access ReflectionObject?
Also, note that you can also import these namespace-less classes if you don't want to prefix every reference with a \
namespace Marketplace\Ebay;
use \ReflectionClass;
class Feedback extends \Ebat\Feedback\EbatNsFeedback_Environment {
//simple example
public function new_ReflectionClass($obj){
return new ReflectionClass($obj);
}
}

PHPUnit constraints extension gives error "PHPUnit_Util_Type::export()" not found

I want a mock object that can tell me if:
when one of its methods are called
that one of the arguments passed to that method
is an array
and has a particular key/value pair.
I want to use PHPUnit's constraints to achieve, this, so my test code would look like this:
$mock = $this->getMock('\Jodes\MyClass');
$mock->expects($this->once())
->method('myMethod')
->with(
$this->logicalAnd(
$this->isType('array'),
$this->arrayHasPair('my_key', 'my_value'),
)
);
// ... code here that should call the mock method
In this previous SO question, the guy ended up writing his own constraint.
I found this library which seems to implement quite a few nifty things. So I installed it by adding this line in my composer.json's require section:
"etsy/phpunit-extensions": "#stable"
But when I try using it, I get an error. I use it like so:
class MyClassTest extends PHPUnit_Framework_TestCase {
public function arrayHasPair($key, $value){
return new PHPUnit_Extensions_Constraint_ArrayHasKeyValuePair($key, $value);
}
public function testmyMethod(){
// code as per my example above
}
}
But that produces this error:
PHP Fatal error: Call to undefined method PHPUnit_Util_Type::export() in C:\MyProject\vendor\etsy\phpunit-extensions\PHPUnit\Extensions\Constraint\ArrayHasKeyValuePair.php on line 50
This previous question/answer explains what the problem is, but I'm not sure what I should do about it. Does this mean that the developers of that library have abandoned it? Is there an alternative to use? Or what options do I have for fixing it? I'm amazed such basic constraints still don't exist in PHPUnit. Obviously I could write my own constraints but surely that's unnecessary?
The PHPUnit_Util_Type::export() method was removed a while ago. The extension you want to use has to be updated to be compatible with current versions of PHPUnit.

PHP class not found in autoloaded implementation file

I really hope this isn't a duplicate, but here I go:
I use Zend's autoloader to load classes. It seems to work, at least it loads the correct file when instantiating my class (Common_TestTest) which is implemented in Common/TestTest.php. But then I get the following error message:
"Class Common_TestTest could not be found in Common/TestTest.php."
There's nothing in TestTest.php other than the class:
<?php
class Common_TestTest extends PHPUnit_Framework_TestCase
{
public function testTesting() {
$this->assertTrue(true);
$this->assertFalse(true);
}
}
I tried dumping get_declared_classes at the end of the file, everything looks fine, Common_TestTest is one of the declared classes - but the exception is still thrown when leaving the file.
The funniest bit is: When I change the name of the class from Common_TestTest to TestTest the same things happens - only that the error message states the name of the missing class as "TestTest". So it definitely sees the class and reacts to it's presence.
There are 2 possibilities that come to my mind as to what causes the problem:
You have some case-mismatch between class-name and file-name, e.g. Testtest.php
When registering the Namespace you use "Common" instead of "Common_". Zend_Loader_Autoloader does not distinguish between PHP 5.3 style namespaces and ZF-style namespaces (rather Prefix). By appending the underscore you make sure, that your namespace is interpreted as a class-prefix rather than a real namespace.

Categories