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.
Related
Imagine a class A istantiating a class B.
I'm trying to write a test for class A and mock the behavior of B.
B could throw some exception when istantiated, and that's what I want to mock with phpunit so as to test A properly.
Is it possible to mock the magic function __construct in some way?
Not with PHPUnit's mocking capability. You would need to redefine the class in your test so that B's __construct throws an exception. Except that this could cause errors to be thrown later when you are trying to test B or you want B to actually be constructed due to the class already being defined. These difficulties are a code smell that your code is not optimally designed.
Rather what you should be doing is passing B into A's __construct method. This is Dependency Injection. So you would create a mock of B and pass it in to A. This is clearer because if B's __construct throws an exception, this is a problem with B and not A. In general, you should avoid using new within your objects and instead pass them in (except in the case of Factory classes).
My opinion you have badly design your classes and a test case of a class must be always related to the class itself: you should not test a class B in a test for class A.
PHPUnit is not able to implement a test of this type but you could use another library (Mockery), that helps you to test this kind of dependencies. With Mockery you can do something like:
$mock = \Mockery::mock('MyClass');
$mock->shouldReceive('name_of_method_1')
->once()
->with($arg1)
->andReturn('return value');
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 .
I am beginning to write tests using Laravel 4.2, PHPUnit, and Mockery.
How can I assert that a Mock Facade was called/run by the Controller that I am testing?
For example, I've got the following Mockery for Queue. My controller doesn't return anything that has to do with the Queue::push(), so is there a way I can assert that it triggered Queue::push()?
Queue::shouldReceive('push')->once();
Queue::shouldReceive('connected')->once();
If I comment out the Queue::push() in my controller, the Queue::shouldReceive throws an exception:
Mockery\Exception\InvalidCountException: Method push() from Mockery_0_Illuminate_Queue_QueueManager should be called exactly 1
times but called 0 times.
Is the only way to assert that the Mock Facade ran is to do something like the following?
$queuepushran = false;
try {
Queue::shouldReceive('push')->once();
Queue::shouldReceive('connected')->once();
$queuepushran = true;
} catch (\Mockery\Exception\InvalidCountException $e) {
//Queue threw an exception
$queuepushran = false;
}
$this->assertTrue($queuepushran);
You're on the right track with using mock objects and expectations to test this, but this isn't quite how mocks should be used. Your code snippet seems to be missing actually calling the controller, so it's hard to tell exactly what's going on, but I'll just run over a full example.
You want to test that when your controller method runs, the Queue facade's push() and connected() methods are called once each. What we want to do is use Laravel's cool facade functionality to replace the real QueueManager class behind the Queue facade with a mock one, that way when the controller calls Queue::push(..), it will be calling our mock object instead.
So, lets start building the test:
public function testSomeControllerMethodCallsTheQueue()
{
Queue::shouldReceive('push')->once();
Queue::shouldReceive('connected')->once();
}
We've now swapped out the real QueueManager for our mock one, and set up how we expect them to be called. Since each test should only be doing one thing, we've now done all we need to do to 'arrange' the test.
Next, we need to 'act' by calling the controller.
public function testSomeControllerMethodCallsTheQueue()
{
Queue::shouldReceive('push')->once();
Queue::shouldReceive('connected')->once();
$this->action('GET', 'YourController#method');
}
The last step is to 'assert', but we don't actually need to do anything. If our Mockery expectations are met, the test will complete and go green. If our Mockery expectations are not met, an exception will be thrown and the test will go red. The exceptions that Mockery throws are intended to be what fails your test, you don't need to do assertions like when comparing the return value or response with what you expect -- Mockery handles all that.
I'm writing a unit test that relies on an external class, exceptionManager. I want to be able to predict what some specific functions on this class will return, so I'm using a mock object. The code is quite straightforward:
$mockExceptionManager = $this->getMock('exceptionManager');
The trouble is, my exception manager implements the IteratorAggregate interface, which requires a method that looks like this:
public function getIterator()
{
return new ArrayIterator($this->exceptions);
}
When I run the unit test, I get the following error:
Fatal error: Cannot redeclare Mock_exceptionManager_ae79bad2::getIterator() in /Applications/MAMP/bin/php5.2/lib/php/PEAR/PHPUnit/Framework/MockObject/Generator.php(170) : eval()'d code on line 297
I have a feeling that the PHPUnit mock object suite also implements the IteratorAggregate interface, and the two are clashing, although I'm unsure. I also tried using the Iterator interface, but ran into the same issue. How can I get around this?
I disabled autoloading on the mock object which solved the issue.
$mockExceptionManager = $this->getMockBuilder('exceptionManager')
->disableAutoload()
->getMock();
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