I recently updated PHPunit from 5.3 to 5.5 in an IntegrationTestCase of an app that is CakePhp 3.x based. and I don't understand how to update my mock generation scripts.
Originally I created my mock like this:
$stub = $this->getMock('SomeClass', array('execute'));
$stub->method('execute')
->will($this->returnValue($this->returnUrl));
After the change to PHPUnit 5.5 this got me the following warning:
PHPUnit_Framework_TestCase::getMock() is deprecated,
use PHPUnit_Framework_TestCase::createMock()
or PHPUnit_Framework_TestCase::getMockBuilder() instead
In order to fix this warning I changed the mock-generation to:
$stub = $this->getMockBuilder('SomeClass', array('execute'))->getMock();
$stub->method('execute')
->will($this->returnValue($this->returnUrl));```
Now I get the following error message when running the test:
exception 'PHPUnit_Framework_MockObject_RuntimeException'
with message 'Trying to configure method "execute" which cannot be
configured because it does not exist, has not been specified,
is final, or is static'
Anybody know, how to avoid this error? Thank you.
PHPUnit_Framework_TestCase::getMockBuilder() only takes one (1) argument, the class name. The methods to mock are ment to be defined via the returned mock builder objects setMethods() method.
$stub = $this
->getMockBuilder('SomeClass')
->setMethods(['execute'])
->getMock();
See also
PHPUnit Manual > Test Doubles > Mock Objects
I will leave this as an answer to myself, when i reach this problem again:
The mocked method may not be private.
First of all, it's just
$stub = $this->getMockBuilder('SomeClass')->getMock();
Second, error states that method execute does exist in your class SomeClass.
So, check if it really exists and it's public and not final.
If everything's good, check a full classname, if it real and specified with correct namespace.
To avoid stupid errors with classname, it's better to use this syntax:
$stub = $this->getMockBuilder(SomeClass::class)->getMock();
In this case, if SomeClass doesn't exist or namespace is missed, you will get a clear error about it.
Addition to upper messages: split mock method declarations
Instead of this:
$mock
->method('persist')
->with($this->isInstanceOf(Bucket::class))
->willReturnSelf()
->method('save')
->willReturnSelf()
;
Use this:
$mock
->method('persist')
->willReturnSelf()
;
$mock
->method('save')
->willReturnSelf()
;
Perhaps , the method does not exist in the class that you mock .
Related
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.
These are my mocks:
$this->user->method('getCustomerId')
->willReturn($this->customerId);
$this->userToken->method('getUser')
->willReturn($this->user);
$this->securityContext->method('getToken')
->willReturn($this->userToken);
$this->securityContext->expects($this->once())
->method('isGranted')
->will($this->returnValue(true));
And this is the code of the class I am testing:
$token = $securityContext->getToken();
$isFullyAuthenticated = $securityContext->isGranted('IS_AUTHENTICATED_FULLY');
It throws the error:
Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException: The security context contains no authentication token. One possible reason may be that there is no firewall configured for this URL.
I have no idea what to do at this point, I though mocks intercepted the call to methods and returned whatever I wanted. But in this case is seems the isGranted methods is not being mock
I figured out that the problem is that the isGranted method is final, so it's imposible to mock, the workaround the problem was to mock all the attributes of SecurityContext so that when the method is call it returns my desired output, instead of intercepting the call with a mock method.
Try using willReturn instead of will and add the with
So try this:
$this->securityContext->expects($this->once())
->method('isGranted')
->with('IS_AUTHENTICATED_FULLY')
->willReturn(true);
Instead of this:
$this->securityContext->expects($this->once())
->method('isGranted')
->will($this->returnValue(true));
Hope this help
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.
In a Yii project, I am using phpunit with getMockbuilder. When I run unit tests on the whole file, they all pass. However, when I do phpunit --filter testMyFunction, I get the following error. "Call to undefined method Mock_Account_3a811374::__construct() ..."
After doing a little more checking I see that if the --filter ends up including a test that does not use the mock in addition to the one that does, then it works fine.
Anyone have any ideas on how to fix it?
Here's some of my code (simplified) ...
use components\Account;
class UtilsTest extends CDbTestCase
{
...
public function testMyFunction()
{
$accountStub = $this->getMockBuilder('Account')
->disableOriginalConstructor()
->setMethods(array('methodToStub'))
->getMock();
$accountStub->expects($this->any())
->method('methodToStub')
->will($this->returnValue(false));
$accountStub->__construct();
...
}
}
I'm confused by what are are trying to do with $accountStub->__construct() when you have specified that you don't want to call the original constructor with ->disableOriginalConstructor()?
You can call the constructor yourself and pass parameters with setConstructorArgs(array $args). So this would look something like this:
$accountStub = $this->getMockBuilder('Account')
->setConstructorArgs($args)
->getMock();
However it wouldn't make sense to call this at the same time as disableOriginalConstructor(). I think you probably want to do one or the other.
I believe the error message you are getting is just PHP telling you that you are trying to do something that doesn't make sense. You are trying to call the constructor on an object that has already been constructed. What's more, you have even specifically told the mock to skip the call to the constructor method. PHP is just telling you that this method does not exist.
I think you probably just need to remove the following line and test again:
$accountStub->__construct();
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